Javascript-форум (https://javascript.ru/forum/)
-   Angular.js (https://javascript.ru/forum/angular/)
-   -   из $Scope в функцию (https://javascript.ru/forum/angular/38912-iz-%24scope-v-funkciyu.html)

MaximusFT 07.06.2013 14:40

из $Scope в функцию
 
Изучаю Angular.

Я только делаю первые шаги, и сейчас делаю небольшое приложение, но дело в том что на многих страницах есть одинаковые $scope и соответственные функции.

Пример
$scope.items = Users.query(function(data){
		$scope.paginator.setPages($scope.items.length);
		var i = 0;
		angular.forEach(data, function(v,k) {
			data[k]._id = i++;
		});
	});

или
$scope.sortBy = function() {
		var order = [];
		angular.forEach($scope.tablehead, function(h){
			if (h.sort>0) order[h.sort-1] = h.name;
			if (h.sort<0) order[Math.abs(h.sort)-1] = '-'+h.name;
		});
		return order;
	};


Но дубляж кода не есть хорошо, решил уменьшить код, сделать функции из этих скопов, но ни в какую, только ошибки в консоли в формате что не может вызвать $scope из функции пишет что "TypeError: Cannot call method 'slice' of undefined..."

slice это метод, он есть если вызывать $scope из примеров внутри контроллера...

Подскажите как можно сделать из Скопа - функцию чтобы внутри нее можно было вызывать другие Скопы и с ними работать.

just-boris 07.06.2013 22:20

Для начала, в angular есть встроенный сортировщик - orderBy.
А насчет ошибки, расскажите, пожалуйста, как вы вызываете функцию и саму эту функцию.

MaximusFT 07.06.2013 23:25

Контроллер страницы
function sortReorder(col,e,scope) {
	if (e.shiftKey) {
		var sortIndex = 0;
		angular.forEach(scope, function(el) {
			if (Math.abs(el.sort)>sortIndex) sortIndex = Math.abs(el.sort);
		});
		angular.forEach(scope, function(el) {
			if (el.name==col) el.sort = el.sort?-el.sort:sortIndex+1;
		});
	} else {
		angular.forEach(scope, function(el) {
			if (el.name==col) el.sort = el.sort>0?-1:1; else el.sort = null;
		});
	}
};
function UsersCtrl($scope, $routeParams, $location, Users) {
	$scope.items = Users.query(function(data){
		$scope.paginator.setPages($scope.items.length);
		var i = 0;
		angular.forEach(data, function(v,k) {
			data[k]._id = i++;
		});
	});

	$scope.selected  = [];
	$scope.paginator = {
		count: 20,
		page:  1,
		pages: 1,
		setPages: function(itemsCount){ this.pages = Math.ceil(itemsCount/this.count); }
	};
	
	$scope.sortBy = function() {
		var order = [];
		angular.forEach($scope.tablehead, function(h){
			if (h.sort>0) order[h.sort-1] = h.name;
			if (h.sort<0) order[Math.abs(h.sort)-1] = '-'+h.name;
		});
		return order;
	};
	$scope.sortReorder = function(col,e) { sortReorder(col,e,$scope.tablehead);	};
}


Обратите внимание на sortReorder, это удалось превратить в функцию... и вызывать ее в разных контроллерах...
А вот из вот этого
$scope.sortBy = function() {
		var order = [];
		angular.forEach($scope.tablehead, function(h){
			if (h.sort>0) order[h.sort-1] = h.name;
			if (h.sort<0) order[Math.abs(h.sort)-1] = '-'+h.name;
		});
		return order;
	};


не получается, и вот из этого в особенности хочется, так как это то что может правильно перегрузить содержимое таблицы на странице
$scope.items = Users.query(function(data){
		$scope.paginator.setPages($scope.items.length);
		var i = 0;
		angular.forEach(data, function(v,k) {
			data[k]._id = i++;
		});
	});


Оба кода уникальны для всех страниц, сейчас у меня их уже 8... и в каждом контроллере страницы есть одинаковые куски кода...

Я понимаю что может я говорю как Ламер... но я очень хочу разобраться...

just-boris 08.06.2013 01:07

я правильно понял, что вы хотите вызвать sortReorder в функции $scope.sortBy?

Shitbox2 08.06.2013 09:47

Кажется, что-то тут не так... $scope только для чтения в шаблонах и только для записи в контроллере. Вы же в своих функциях используете область видимости для получения данных. Тогда уж нужно присвоить модель переменной и работать уже с ней.
var model = Users.query(function(data){
$scope.items = model

Возможно, имеет смысл создать собственный фильтр и использовать его в контроллере с помощью сервиса $filter

Shitbox2 09.06.2013 15:05

Еще перевел статью по областям видимости: http://www.angular.ru/guide/understanding_scopes. Там как раз говорится о том, как пользоваться областями

DjDiablo 10.06.2013 07:38

Не знаю правильно ли я понял вопрос

Но если у тебя есть общие куски кода в нескольких контролёрах тогда создайте собственный модуль с нужными методами и подключайте его в тех контролёрах где он вам необходим. Это обыкновенное модульное программирование.

вот кстатии хороший пример. http://www.angular.ru/guide/dev_guid...ng_controllers

Сервис предоставляющий множество методов можно как нибудь так описать
angular.module('MyServiceModule', []).
 factory('notify', function() {
    
    return {
         fync1:function(){},
         fync2:function(){},
         fync3:function(){}
    }

  });


либо

angular.module('MyServiceModule', []).
 factory('notify', function() {
    var f=function(){
       //бла бла
    },
    p=f.prototype;
    p.func1=function(){};
    p.func2=function(){};

    return new f();
    //return f; если хочешь сам вызывать конструктор из контрола
  });


3) Можно создать сервис users который будет возвращать не только данные но и методы работы с ними.(вероятно идеальное решение)

4) Придумал ещё один ход подобный примесям. Но сам бы я его пожалуй избегал до последнего.

angular.
 module('MyServiceModule', []).
 factory('extendScope',function() {    

    return function(scope) {
        scope.fn1=function(){};
        scope.fn2=function(){};
        scope.fn3=function(){};
        return scope;
    }   
  });
function myController(scope, extendScope) {
  extendScope(scope);
  // или scope=extendScope(scope);
}

MaximusFT 11.06.2013 15:03

Спасибо большое за советы, наверное мне не хватает банальных знаний об Объектах.
Можно еще один вопрос:

<div class="span6" ng-controller="MyCtrl14">
	MyCtrl14 > block14 = 
	<br>
	<button ng-click="aMyCtrl14()">- ! -</button>
	<br>
	{{block14}}
	<div class="row show-grid">
		<div class="span3">
			IndexCtrl > block141 =
			<br>
			{{block141}}
		</div>
		<div class="span3" ng-controller="MyCtrl142">
			MyCtrl142 > block142 =
			<br>
			<button ng-click="aMyCtrl142()">- ! -</button>
			<br>
			{{block142}}
		</div>
	</div>
</div>

function MyCtrl14($scope, notify) {
	$scope.block141 = '111';
	$scope.aMyCtrl14 = function(){
		$scope.block141 = '333';
		$scope.block142 = '444';
	}
}
function MyCtrl142($scope, newnoty) {
	$scope.aMyCtrl142 = function(){
		$scope.block141 = '555';
		$scope.block142 = '666';
	}
}


1) Нажимаем на кнопку "aMyCtrl14" - она вставляет оба значения block141 и block142.
2) Нажимаем на кнопку "aMyCtrl142" - она обновляет только значение block142.
Понятно так как дочерняя не имеет по умолчанию возможности работать с областью родителей. НО!
3) Нажимаем на кнопку "aMyCtrl14" - она обновляет только значение block141.
Почем Родитель теряет власть над областью после того как в нее было что-то записано дочерним контроллером...

Подскажите как можно обновлять информацию из одного контроллера в другом?

just-boris 15.06.2013 17:02

Потому что пока значение не определено, оно ищется у родителей вверх по цепочке. Потом оно перезаписывается локальным и больше не зависит от родителей.

MaximusFT 15.06.2013 17:19

А принудительно как внести изменения в дочернем элементе?
Может проще отказаться от 2-х Контроллеров и делать все в одном?

DjDiablo 16.06.2013 10:24

На данный момент в портируемом проекте я использую один контроллер на страницу, всё остальное реализовано через директивы, все взаимосвязи указаны в атрибутах директив.

В твоём случае доступ к scope родителя можно получить через $scope.$parent если не ошибаюсь, после изменения parent можно опубликовать изменения при помощи $scope.$parent.$apply()

Shitbox2 16.06.2013 10:50

Цитата:

Сообщение от MaximusFT (Сообщение 255222)
Подскажите как можно сделать из Скопа - функцию чтобы внутри нее можно было вызывать другие Скопы и с ними работать.

Вот так делать не стоит. Поначалу тоже что-то подобное мутил. В итоге намухлевал с иерархией областей видимости, пробовал перемещаться по областям через свойства $parent, lastChild и т.п. И получил кучу проблем. Тут самое верное решение разделять данные через сервис, пусть даже его придется 100500 раз указывать в зависимостях.
$scope.paginator.setPages($scope.items.length);

Такого быть не должно. Нужно как-то так:
myService.paginator.setPages(items.length);

$scope только для передачи данных в вид и такое его использование рано или поздно приведет к проблемам.

Также можно использовать механизм событий
$emit('MyEvent') и $broadcast('MyEvent')

И писать как-то так
$scope.$on('MyEvent', function() {
    $scope.count++;
  });

Ну или просто передавать данные через $rootScope и подписываться на них через $watch

DjDiablo 16.06.2013 12:25

Цитата:

Подскажите как можно сделать из Скопа - функцию чтобы внутри нее можно было вызывать другие Скопы и с ними работать.
Присоеденяюсь к Shitbox. Сделать так можно но делать этого не стоит.

Нужно стремится по возможности к слабому связыванию компонентов. Это аксиома проектирования в любом языке, любом фреймворке, не зависимо от того какую программу вы пишете.

Из красивых решений это атрибуты директив, либо события. Атрибуты деректив это в каком то смысле шлюзы компонентов, и через такие шлюзы можно относительно безопасно состыковывать компоненты UI между собой.

Можно иметь общие данные для обсолютно независимых котролов и деректив, но придётся их хранить в сервисе и подключать сервис там где нужен доступ к этим данным. Вместе с данными в сервисе желательно хранить и методы обработки этих данных, такие методы можно будет вызывать из любого контрола где используется этот сервис (в 7ом посте писал об этом). Сделать сервис частью scope тоже труда не составляет.

Сервис конечно не обеспечивает синхронизацию данных между различными компонентами, иными словами если один контроллер изменит данные в сервисе, то другой контроллер об этом изменении не узнает. Если нужна синхронизация то в качестве решения может проканать rootScope. Но так как обьект глобальный то злоупотреблять им не стоит, если всё пихать в rootScope то при изменении одних данных будет дёргаться слишком много лишних обработчиков watch. Довольно плохо если объекты и свойства в rootScope будут создаваться не централизованно а размазано по различным контроллерам, такое приложение будет сложно понимать.

Из любопытных идей мне на ум приходит создание сервиса со своим cобственным scope. Такой финт позволит сочетать преимущества сервиса, с возможностью синхронизации при изменении данных при этом избегая универсального глобального объекта $rootScope. Получится что то вроде "активной модели". Однако в angular потребности в подобных инструментах у меня пока не возникало.

Собрать же кучу скопов различных компонентов в одной функции это возможно худшее из всех возможных решений.

MaximusFT 17.06.2013 09:42

Огромнейшее спасибо!
Буду переваривать информацию... так как скорее всего еще не до конца понял точку применения AngularJS.
От сюда возник вопрос, но уже в новой теме.


Часовой пояс GMT +3, время: 20:32.