Как стать автором
Обновить

Комментарии 36

Больше библиотек, хороших и разных. Rivets.js не смотрели?

при генерации inputa, хотя в нем и установлено его значение, но angular данное значение не учитывает и берет значение «модели»

А почему было не написать кастомную директиву для Angular, которая ведет себя как надо? Это не нападки, я просто плохо представляю ангуляр.
Я на vue.js делал метод для загрузки данных в модель с сервера. Ничего особенно сложного.
Да, Vue.js хорош
01) Одностороннее связывание данных

blog.thoughtram.io/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html
И ещё angular 2.
но angular данное значение не учитывает и берет значение «модели», которое при загрузке страницы является пустым.

Так в чём проблема инициализировать модель нужными данными?

html-код элемента находится в одном месте, шаблон его отображения в другом, а контроллер вообще где-то в скриптах в начале страницы

Ангуляр не навязывает структуру приложения как, например, Ember. Хочешь писать всё в одном месте — пиши (к тому же в 1.5 появятся компоненты). Но обычно 1 компонент хранится в 1 папке. Сразу всё становится понятно. Ещё можно использовать es6 с модулями.

03) Динамическая загрузка содержимого

это использование $scope.$apply() в контроллере.

Что?

function FooController($http) {
  var vm = this;

  vm.getUser = getUser;

  function getUser() {
      $http.get('/api/user/1')
        .then(function (response) {
            vm.user = response.data;
        });
  }
}

И в шаблоне:
<div ng-controller="FooController as vm">
    <a href ng-click="vm.getUser()">get user</a>
    <pre>{{ vm.user | json }}</pre>
</div>

Плюс, есть ng-if, который даже DOM не загрузит, если условие не выполнится.

В общем, библиотека может и ок, но я не понимаю, зачем критиковать ангуляр даже не разобравшись в основах?
Не путайте, пожалуйста, единоразовое связывание (one-time) данных с однонаправленным (one-way).
Про инициализацию (ng-init) — пытался, в статье сказано: возникли проблемы при использовании виджета Editable, а именно после сохранения значения на сервере и попытке обновления связей, он вновь «скидывал» на значение в «ng-init».
Про контроллер — наверное, вы правы, но как поведет себя система, если внутри загруженных данных также будут данные, которые нужно «связывать», причем возможно даже при помощи ajax-загрузки.
Зачем вы используете ng-init? Контроллер то зачем вам?
В работе я использую серверный фреймворк yii2, который генерирует html-код и в input'ы ставит соответствующие значения из базы. Инициализация (ng-init) мне нужна, поскольку установленное в input значение angular не учитывает, а берет значение своей модели:
    <div>
      <label>Имя:</label>
      <input type="text" value="ЭтоНеВидит!!!" ng-model="yourName">
      <hr>
      <h1>Привет, {{yourName}}!</h1>
    </div>

Так получайте JSON и рендерите, зачем тогда вообще SPA?
ng-value, не?
как поведет себя система, если внутри загруженных данных также будут данные, которые нужно «связывать», причем возможно даже при помощи ajax-загрузки.

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

Ну а одностороннее связывание можно организовать через директивы, изолированные скоупы и '&'. Или можно попробовать организовать своё приложение используя архитектуру flux.
Не хочу писать много, похоже вы не особо пытались разобраться в ангуляре, а сразу начали его «чинить».

как поступать, когда в загруженном содержимом присутствуют блоки, содержимое которых также загружается или генерируется динамически?


Каждый такой «динамический» блок оформляется директивой, и далее директива сама думает где взять данные для себя (отдельный запрос, reduce из пачки данных, что-то реактивное и тд) и что с ними делать дальше.

к тому же в 1.5 появятся компоненты

Компоненты, которые по сути простая обертка над директивой.
Поверьте, разобраться я очень даже пытался. Один день потратил на Angular, два дня на Angular Light, и еще день на другие фреймворки.
И с директивами тоже пытался. Но, вы правы, возможно, недостаточно глубоко.
Один день?))) А свою библиотеку сколько писали? Да и ещё официальный сайт)))
Я считаю, что полного рабочего дня для поиска в интернете решения конкретной проблемы более чем достаточно. В общей сложности на поиски готового решения ушла где-то неделя.
Библиотеку написал за неделю. Столько же времени ушло на сайт, но на сайт больше на «украшательство», нежели чем на функционал.
а при обновлении связывания, вновь берется значение из «ng-init», которое уже не является актуальным. Беда…
Это не так, ng-init/al-init срабатывает только на старте.

решил все же не плодить лишних параметров, ведь простота — залог успеха.
Я далеко не спец в Angular, и возможно, есть способы выполнить данные задачи. Но скорей всего это будет далеко не просто и очень громоздко.
можно решить через костыли и груды ненужного кода
Я бы не сказал что на RainyJs код выглядит проще, вот сравнение примеров из статьи.

RainyJs
<input rxname="var05" value="2" type="number"><br>
<div rxdata="var05" rxcode="
    if(value < 0){ self.classList.add('red'); }
        else { self.classList.remove('red'); }
    return 'Введено значение: ' + (value||0);
"></div>

Вариант на Angular Light:
<input al-value="var05" type="number"><br>
<div al-class="red: var05 < 0">Введено значение: {{var05}}</div>
Пример

RainyJs
<div><label>Кол-во: </label><input type="number" rxname="m_count" value="0" /></div>
<div><label>Цена: </label><input type="number" rxname="m_price" value="0"
rxdata="m_count m_summa"
    rxcode="
    if(rxname.toLowerCase() !== 'm_summa')return undefined;
    return ((values['m_count']||0) != 0)
        ? (values['m_summa']||0) / values['m_count'] : 0;
    "/></div>
<div><label>Сумма: </label><input type="number" rxname="m_summa" rxdata="m_count m_price" value="0"
rxcode="return (values['m_count']||0) * (values['m_price']||0);"/>
</div>

Вариант на Angular Light:
<div><label>Кол-во: </label><input type="number" al-value="m_count" (input)="m_sum=m_price*m_count" /></div>
<div><label>Цена: </label><input type="number" al-value="m_price" (input)="m_sum=m_price*m_count" /></div>
<div><label>Сумма: </label><input type="number" al-value="m_sum" (input)="m_price=m_sum/m_count"/></div>
Пример

Так же в RainyJs похоже нет работы с массивами, простое todo-mvc не сделать. Да и вообще если примеры чуть усложнить, то RainyJs уже не справляется. Видимо это из-за молодости проекта.
Кстати Angular Light достаточно гибкий, вот ваш первый пример который работает на Angular Light:
<label>Name:</label><br>
<input rxname="var01" value="World"><br>
Hello, <span rxdata="var01"></span>!
Пример
Вы, наверное, разработчик Angular Light? Именно ваша библиотека показалась мне наиболее интересной, очень долго пытался именно на ней реализовать необходимый мне функционал.
Немного не в тему: у меня не работало обновление через alight.bootstrap() после загрузки ajax-содержимого. Разбирая ваши исходники выяснил, что дело во флаге ma_bootstrapped, поэтому для обновления связей делал так (код тестовый):
	tag = $("#id004");
	delete tag[0].ma_bootstrapped;
	$("#b01").removeAttr("al-init");
	alight.bootstrap(tag[0],{"name":value});
у меня не работало обновление через alight.bootstrap()
Это не шаблонизатор, данные привязываются к DOM единожды (т.е. нужно вызывать bootstrap единожды для конкретного DOM), после чего можно менять данные при этом привязанный DOM будет меняться.

Вот пример как отложено менять данные и дочерний html (вместо setInterval можете поставить свой ajax запрос)
Ух, столько комментариев, даже не ожидал. Думаю, лучше я просто опишу проблему, с которой пришлось столкнуться.
Есть веб-страница документа, у которого табличная часть. В табличной части присутствуют колонки Кол-во, Цена, Сумма. Заказчик оправдано сказал, что неудобно считать сумму самостоятельно и нужно сделать, чтобы при изменении Цены или Кол-ва сумма пересчитывалась. Проблема в том, что записи добавляются/изменяются динамически через ajax-запросы. Каким будет ваше решение?

За несколько дней я не смог найти простого и подходящего решения. И написал библиотеку. Решил поделиться ей с миром. И мне не совсем понятна критика уровня «это все есть в Angular». Может и есть. Но если так мыслить, то зачем вообще AngularLight или vue.js, если есть Angular? Если моя библиотека не будет востребована, то через некоторое время она сама собой отомрет, уступив место чему-то более подходящему. И в этом нет ничего страшного, это обыкновенный процесс развития технологий.
я просто опишу проблему, с которой пришлось столкнуться.
При прочтении статьи чувствовалось, что это библиотека для нескольких конкретных кейсов.

Проблема в том, что записи добавляются/изменяются динамически через ajax-запросы. Каким будет ваше решение?
Такие вещи с Angular Light делаются легко, нужна конкретика, примеры ajax результата, вставляется html?

И в этом нет ничего страшного, это обыкновенный процесс развития технологий.
Все правильно, при этом вы ещё получили хороший опыт.
зачем вообще AngularLight или vue.js, если есть Angular?

Просто не всем нужны DI и сервисный слой.

Проблема в том, что записи добавляются/изменяются динамически через ajax-запросы. Каким будет ваше решение?

При изменении/добавлении записи выставляем флаг, что началась загрузка и отправляем запрос к API. После получения ответа убираем флаг и обновляем данные в контроллере.

Как-то так
class FooService {
    /*@ngInject*/
    constructor($http) {
        this.$http = $http;
    }
    
    function getItems() {
        return this.$http.get('/items').then(response => {
            let items = response.data;
            // тут могут быть какие-то действия с полученными данными
            return items;
        });
    }

    function createItem(item) {
        return this.$http.post('/items', item);
    }

    function updateItem(item) {
        return this.$http.put('/items/' + item.id);
    }

    function deleteItem(item) {
        return this.$http.delete('/items/' + item.id);
    }
}

class FooController {
    /*@ngInject*/
    constructor($foo) {
        this.$foo = $foo;
        this.items = [];
        this.isLoading = false;
        this.error = false;

        this.fetchData();
    }

    fetchData() {
        this.isLoading = true;
        return this.$foo.getItems()
            .then(items => {
                this.items = items;
                this.error = false;
            })
            .catch(() => {
                this.error = true;
            })
            .finally(() => {
                this.isLoading = false;
            });
    }

    addItem() {
        this.items.push({amount: 1, cost: 100});
    }

    createItem(item) {
        this.isLoading = true;
        return this.$foo.createItem(item).then(() => {
            return this.fetchData();
        });
    }

    updateItem(item) {
        this.isLoading = true;
        return this.$foo.updateItem(item).then(() => {
            return this.fetchData();
        });
    }

    deleteItem(item) {
        this.isLoading = true;
        return this.$foo.deleteItem(item).then(() => {
            return this.fetchData();
        });
    }
}

angular.module('app', [])
    .service('$foo', FooService)
    .controller('FooController', FooController);

<div ng-controller="FooController as vm">
    <table ng-hide="vm.isLoading">
        <thead>
            <tr>
                <th>ID</th>
                <th>Amount</th>
                <th>Cost</th>
                <th></th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="item in vm.items track by item.id">
                <td><strong>{{ item.id }}</strong></td>
                <td><input type="text" ng-model="item.amount"></td>
                <td><input type="text" ng-model="item.cost"></td>
                <td><a href ng-click="vm.deleteItem(item)">delete</a></td>
                <td><a href ng-click="vm.updateItem(item)">update</a></td>
            </tr>
        </tbody>
        <tfoot>
            <tr>
                <td colspan=5><a href ng-click="vm.addItem()">Add</a></td>
            </tr>
        </tfoot>
    </table>
    <div ng-show="vm.isLoading">
        <img src="loading.gif" alt="">
    </div>
</div>

Вот! Об этом я и твержу. Чтобы реализовать простейшее связывание вида «цена-количество-сумма» мне пришлость бы генерировать html-код на Angular. А у меня он уже сгенерирован, я использую стандартный yii2-компонент GridView, т.е. мне нужно без всяких ng-repeat, просто связать поля ввода!

А теперь как это сделать на RainyJs:
01) в поле цены и поле суммы добавляем rxname=«priceX» и rxname=«countX», где X — номер или индекс строки
02) в поле суммы добавляем rxdata=«priceX countX» и код функции rxcode=«return priceX*countX»
03) после загрузки обновленного содержимого вызываем rainy($("#GridId"))
И все! Без всяких контроллеров, директив, шаблонов и т.д.

А вообще не ожидал я такого от хабра. Столько отзывов и ниодного положительного, одна критика, причем необоснованная. Хотя продукт, на самом деле, действительно, заслуживает внимания.
Чтобы я еще хоть раз делился своими разработками — нет уж, увольте.
Критика вполне обоснованная.
Задача изначально не для юзкейса ангуляра, т.к. все шаблоны и так грузятся с сервера. Да и сам по себе ангуляр — фреймворк для построения сложных MV*-приложений на фронтенде, а не либа для биндинга атрибутов.
Но нет, это не Вы не умеете инструмент выбирать, а это ангуляр плохой. У него сразу же нашлись недостатки, которых в реальной жизни нет, просто нужно поизучать ангуляр чуть больше 1-го дня.
И тут же началась борьба против ветряных мельниц, которые сами себе и воздвигли. И вместо того, чтобы взять какой-нибудь jquery/knockout и написать всё быстренько на нём, зачем-то началось велосипедостроение.

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

А из статьи я так и не понял, зачем оно и что делает, что рендерит yii, а что подгружается аяксом, и т.д.
Почему же сразу «велосипедостроение»? Я же указал вам конкретную задачу, которая была передо мной поставлена. На текущий момент никто из комментирующих простое решение не показал, никто не показал даже хоть какое-нибудь решение. Зато вот критикуют все кому не лень.

Вот, набросал play-ground: Пример связывания в табличной части
Там эмуляция табличной части и пример моего решения при помощи RainyJs.
Буду рад, если вы предложите свой вариант на Angular, Angular Light или на любом другом фреймворке или библиотеке. Думаю, что достаточно многие программисты сталкиваются с подобной задачей, особенно те, кто используют для разработки серверные фреймворки.

Шо за чушь? Жму Сохранить и оно сбрасывает на дефолт
Прежде чем «шохать» могли бы и на код краем глаза взглянуть.
Это эмулятор загрузки с сервера: те строки, которые были изменены но на которых не была нажата кнопка «Сохранить» возвращаются в исходное состояние при обновлении, поскольку «на сервере» данные не были обновлены.
Буду рад, если вы предложите свой вариант на Angular, Angular Light или на любом другом фреймворке или библиотеке. Думаю, что достаточно многие программисты сталкиваются с подобной задачей, особенно те, кто используют для разработки серверные фреймворки.

Ну на knockoutJS вы можете написать свой binding, который будет подгружать данные из HTML, к которому вы биндите модель. Кажется уже были готовые (в любом случаем это не сложно). Будет выглядеть примерно так:
<input type="text" data-bind="predefined, value: username" value="Василий Петрович"/>

const domNode = document.getElementById('some');
const vm = { username: ko.observable };
ko.applyBindings(vm, domNode);
vm.username(); // Василий Петрович


Про Ajax я так и не понял. Вы ajax-ом получаете HTML формы\таблицы или таки данные для неё? Куда благоразумнее именно данные. Но на худой конец можно заного про-applyBinding-ить форму. Правда не совсем ясно, чего вы тогда желаете от инструмента. Какие цели он должен решать?
Вы ajax-ом получаете HTML формы\таблицы или таки данные для неё
Я использую серверный фреймворк Yii2, в нем есть компонент GridView, который генерирует таблицу. В нем есть возможность «обернуть» данную таблицу в PJAX, чтобы записи добавлялись/изменялись без перезагрузки всей страницы, а только перезагрузкой непосредственно самой таблицы. Т.е. через ajax приходит именно полноценный html-код страницы, загружается, и затем вызывается событие «ТаблицаЗагружена».

А вообще спасибо. Ваши комментарии приятно читать (в них не содержится негатива).
В вашем кейсе не нужен SPA давно не любил такие штуки, когда сервер пытается разрулить фронтенд часть
Вопрос, а что вы хотите при этом от реактивной библиотеки? Какие задачи она должна решать? KnockoutJS можно натянуть на что угодно, при желании. Но я не вижу самой необходимости в чём-то реактивном, если у вас туды сюды именно HTML шастает.
никто не показал даже хоть какое-нибудь решение
Я вам выше привел несколько примеров.

Буду рад, если вы предложите свой вариант
Например так

Думаю, что достаточно многие программисты сталкиваются с подобной задачей
Программисты (которые используют MV* фреймворки) обычно делают SPA поэтому у них не возникает таких проблем.
Да, ваш код почти то, что нужно. Возможно, если бы я тогда «дошел» до него или увидел в примерах, то пытался бы адаптировать именно его, а не разрабатывать свое решение.

Единственное, есть несколько вопросов.
Вы, как я понял, определяете для div'а переменную, затем записываете в переменную полученное содержимое, после чего вызываете scope.$scan() для обновления связей. Нельзя ли выполнить обновления связей без использования переменной? Просто в моем случае загружаемый фрагмент будет очень огромный. Не хотелось бы хранить его в переменной.

И второй вопрос: если в загружаемом фрагменте будут скрипты — они отработают? У меня в табличной части используются виджеты, а они добавляют для себя js-скрипты. Этот вопрос я могу, в принципе, и сам проверить, но лучше спрошу у вас.
загружаемый фрагмент будет очень огромный

Много элементов в массиве? Серверная пагинация в помощь.
Нельзя ли выполнить обновления связей без использования переменной?
Можно

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

если в загружаемом фрагменте будут скрипты — они отработают?
Да должны.
Попробуйте knockoutJS. AngularJS для ваших целей, как из пушки по воробьям.

Честно говоря не понимаю откуда такой ажиотаж по поводу AngularJS и React. Большая часть разработчиков не связана с написанием SPA, но если судить по популярным трендам, складывается ощущение, что каждый первый бложек это RESTful SPA с offline-режимом.

P.S. с KnockoutJS можно и сложные вещи писать, и простые. Но в отличии от Angular-а он не решит за весь все ваши проблемы. Это более узко-заточенный инструмент.
Как-то необычно даже, есть сайт с версиями, а нет публичного репозитория. Хотелось бы изучить код, но качать zip файлики как-то странно. Или я не заметил?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории