Статьи

Создайте микро-блог с SproutCore

SproutCore — это революционная среда JavaScript для создания клиентских приложений настольного класса, которые запускаются в браузере. Этот учебник познакомит вас с основными концепциями SproutCore, разработав простой микроблог типа Twitter.

Этот урок содержит как скринкаст, так и письменный урок!




SproutCore — в отличие от jQuery, который в первую очередь является библиотекой DOM, — это инфраструктура MVC для клиентских приложений настольного класса, написанная на JavaScript и находящаяся под сильным влиянием Cocoa (среда разработки Apple Mac OSX). Он отличается от других библиотек JavaScript тем, что фокусируется на предоставлении всех инструментов, необходимых для создания полнофункционального настольного приложения в браузере, таких как маршрутизация, контроллеры представления, компоненты пользовательского интерфейса, уровень хранилища данных, средства модульного тестирования и развертывания. Использование SproutCore означает перемещение вашей клиентской части бизнес-логики, что значительно уменьшает задержку, потому что вашему приложению нужно только обратиться к серверу за данными (и для выполнения бизнес-логики вам может не понадобиться клиентская сторона).

Использование SproutCore означает перемещение клиентской части бизнес-логики, что значительно уменьшает задержку


SproutCore распространяется как драгоценный камень. Чтобы установить SproutCore, просто запустите sudo gem install sproutcore в Терминале (другие способы установки SproutCore можно найти здесь ). Несмотря на то, что SproutCore является RubyGem и поставляется в комплекте с сервером разработки, после развертывания SproutCore представляет собой не что иное, как чистый HTML, JavaScript и CSS. Чтобы создать приложение с SproutCore, вы используете JavaScript для определения, представления с использованием связанных элементов пользовательского интерфейса, предоставляемых SproutCore, или пользовательские представления. Эти элементы пользовательского интерфейса отображают HTML для нас, а затем с помощью CSS-структуры SproutCore, называемой Chance , мы можем стилизовать пользовательский интерфейс. Используя контроллеры, мы можем связать эти представления с данными в приложении. Наконец, используя модель источника данных SproutCore, мы подключим приложение к CouchDB.


  1. Откройте терминал.
  2. Запустите sc-init microblog создав базовое приложение SproutCore.
  3. cd microblog
  4. sc-server который запускает сервер разработки SproutCore.
  5. Откройте http://localhost:4020/microblog в вашем браузере, и вы должны увидеть «Добро пожаловать в SproutCore!» (Примечание: вам не нужно перезагружать сервер SC каждый раз, когда вы вносите изменения в свой источник, просто обновите окно браузера) .


Давайте рассмотрим каждый файл в сгенерированном источнике SproutCore и объясним, что делает каждая часть. Исходный код начинается в папке apps/microblog .

  • core.js — содержит настройки конфигурации ваших приложений и глобальные синглтоны.
  • main.js — главная отправная точка вашего приложения, он должен установить основной вид, запустить ваш основной контроллер и загрузить необходимые данные для основного вида.
  • resources / loading.rhtml — этот HTML-код отображается во время загрузки и запуска приложения SproutCore, он не виден очень долго, поскольку SproutCore загружается довольно быстро.
  • resources / main_page.js — содержит главную страницу (и ее просмотры), загруженную по умолчанию, кодом, в main.js.

Прежде чем мы сможем приступить к созданию пользовательского интерфейса, приложению SproutCore требуются некоторые данные. Давайте начнем с определения модели. SproutCore поставляется с удобным генератором моделей. Запустите sc-gen model Microblog.posts чтобы сгенерировать файлы JavaScript базовой модели для объектов записей. Нам нужно сообщить SproutCore о конкретных данных, которые будут содержать объекты post. Откройте apps/microblog/models/posts.js и добавьте атрибуты ниже.

1
2
3
4
5
6
Microblog.Posts = SC.Record.extend(
  /** @scope Microblog.Posts.prototype */ {
  
  post: SC.Record.attr(String),
  published: SC.Record.attr(Number)
}) ;

Microblog.Posts расширяет SC.Record и называет данные, в которых должны содержаться объекты SC.Record . Мы используем методы, предоставляемые SproutCore, чтобы определить типы данных, которые мы ожидаем в каждом атрибуте.


В этом уроке мы изначально будем использовать Fixtures для заполнения нашей модели, а не усложнять себе бэкэнд-сервер. На протяжении всей статьи, если мы будем вносить изменения в данные в приложении через пользовательский интерфейс, оно не будет сохраняться, если мы перезагрузим приложение — до тех пор, пока мы не добавим сохранение данных через CouchDB. Откройте apps/microblog/fixtures/posts.js где мы укажем некоторые фиктивные данные; вставьте данные, как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
sc_require(‘models/posts’);
  
Microblog.Posts.FIXTURES = [
{
    guid: 1,
    post: «Wow, Nettuts rocks!»,
    published: 1297986988
},
{
    guid: 2,
    post: «Apple’s MobileMe was built using SproutCore»,
    published: 1297987009
},
{
    guid: 3,
    post: «Checkout this awesome JavaScript application framework called SproutCore»,
    published: 1298159746
}
];

Чтобы убедиться, что наша модель работает правильно, мы можем вызвать методы данных SproutCore из консоли JavaScript. Перезагрузите страницу, откройте консоль и выполните posts = Microblog.store.find(Microblog.Posts) . Это должно загрузить все записи сообщений в переменную posts. Нам нужно getEach переменную posts с getEach метода getEach из SproutCore, чтобы просмотреть что-нибудь полезное. Выполните posts.getEach('post') чтобы просмотреть все сообщения.


Мы собираемся немного продвинуться вперед и создать пользовательский интерфейс. Причина этого заключается в том, что SproutCore использует привязку данных для привязки значений элементов пользовательского интерфейса к объектам JavaScript; Результатом этих привязок является то, что значения в связанных JavaScript-объектах вставляются в HTML, как и следовало ожидать, но с одним отличием: устанавливается связь между его значением и объектом. Любые последующие обновления объекта в JavaScript будут автоматически отражаться в пользовательском интерфейсе. Таким образом, мы собираемся сначала создать пользовательский интерфейс, а затем внедрить контроллер для привязки данных к этому пользовательскому интерфейсу. Давайте resources/main_page.js : откройте файл resources/main_page.js , и давайте предоставим нашему приложению панель навигации верхнего уровня и представление списка.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Microblog.mainPage = SC.Page.design({
    mainPane: SC.MainPane.design({
        childViews: ‘topView postView contentView’.w(), // SC helper method splits string to array.
  
        topView: SC.ToolbarView.design({
            layout: { top: 0, left: 0, right: 0, height: 40 },
            anchorLocation: SC.ANCHOR_TOP
        }),
  
        postView: SC.View.design({
            childViews: ‘form’.w(),
            layout: { top: 40, height: 75 },
            backgroundColor: ‘white’,
  
            form: SC.View.design({
                    layout: { left: 200, right: 200 },
                    backgroundColor: «black»
            })
        }),
  
        contentView: SC.ScrollView.design({
            hasHorizontalScroller: NO,
            layout: { top: 115, bottom: 0, left: 0, right: 0 },
            contentView: SC.ListView.design({
  
            })
        })
    })
});

Ваше приложение SproutCore может иметь более одной страницы, но здесь вы можете видеть, что мы определили только одну страницу. На этой странице мы определяем три дочерних представления; мы определили их обоих в childViews а затем указали их содержимое в объекте. topView мы определяем как ToolbarView и привязываем его к верхней части страницы. Вы заметите, что мы не указали ширину; это фактически говорит SproutCore, что мы хотим, чтобы он заполнил доступное пространство и изменил размер в окне браузера.

Далее, postView мы определяем как стандартный View , который имеет полную ширину, и присваиваем ему одну form sub view. Это будет содержать форму публикации микроблога. Мы не указываем ширину, но мы указываем, что представление должно быть 200 пикселей справа и 200 пикселей слева, делая представление более узким, чем окно браузера, но сохраняя функцию автоматического изменения размера. Основной контент находится в contentView , который мы определили как ScrollView . Содержимое этого ScrollView будет ListView , которое будет содержать наши сообщения в микроблогах.



Теперь, когда мы сделали начальный макет, давайте добавим форму, позволяющую пользователю добавлять посты.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Microblog.mainPage = SC.Page.design({
    mainPane: SC.MainPane.design({
    childViews: ‘topView postView contentView’.w(), // SC helper method splits string to array.
  
    topView: SC.ToolbarView.design({
        childViews: «appName».w(),
        layout: { top: 0, left: 0, right: 0, height: 40 },
        anchorLocation: SC.ANCHOR_TOP,
  
        appName: SC.LabelView.design({
            layout: { top: 5, left: 5, width: 100 },
            displayValue: «Microblog App»,
            layerId: «mb-logo» // HTML id attribute
        })
    }),
  
    postView: SC.View.design({
        childViews: ‘form’.w(),
        layout: { top: 40, height: 150 },
        backgroundColor: ‘white’,
  
        form: SC.View.design({
            childViews: ‘postContent post’.w(),
            layout: { left: 200, right: 200 },
  
            postContent: SC.TextFieldView.design({
                layout: { top: 10, left: 10, right: 10, height: 60 },
                isTextArea: YES,
                hint: «What’s on your mind?»
            }),
  
            post: SC.ButtonView.design({
                layout: { top: 80, right: 10, width: 100 },
                title: «Post»,
                isDefault: YES
            })
        })
    }),
  
    contentView: SC.ScrollView.design({
      hasHorizontalScroller: NO,
      layout: { top: 150, bottom: 0, left: 0, right: 0 },
      contentView: SC.ListView.design({
  
      })
    })
  })
});

Первое, что мы добавили, это SC.LabelView на панель инструментов с названием «Приложение Microblog». Затем мы изменили подпредставление form включив в него два дочерних представления, первое postContent , которое представляет собой SC.TextFieldView и isTextArea что делает его многострочным SC.TextFieldView isTextArea . Во-вторых, мы добавили SC.ButtonView , который назначит действие, которое добавит сообщение при его нажатии. Оба они являются стандартными элементами интерфейса SproutCore. SproutCore имеет полный каталог пользовательского интерфейса, взгляните на пример кухонной мойки, чтобы понять, что входит в комплект. Все эти элементы могут быть тематизированы с помощью CSS с использованием каркаса тем SproutCore.



Давайте SC.ListView данные постов в Microblog.Posts.FIXTURES с SC.ListView в приложении, чтобы мы могли видеть посты. Сначала нам нужно создать контроллер. Запустите генератор SproutCore, как SC.ArrayController ниже, чтобы получить шаблон SC.ArrayController .

1
sc-gen controller Microblog.postsController SC.ArrayController

Нам не нужно ничего делать с сгенерированными файлами контроллера в данный момент, поэтому мы собираемся оставить их в покое. SC.ArrayController и SC.ObjectController фактически действуют как прокси для их содержимого под капотом SproutCore. Вы можете связать напрямую с контроллером, как если бы вы были привязаны к его содержимому. Как вы увидите через мгновение, мы установим содержимое этого контроллера в сообщения. Этот контент контроллеров (сообщения) будет привязан к SC.ListView , который будет их отображать. Поскольку контроллер действует как прокси, мы можем установить новый массив сообщений для содержимого контроллеров (скажем, сообщения 10-20), и пользовательский интерфейс будет автоматически обновляться. Любые изменения, которые также вносятся в базовый набор данных (массив записей), будут автоматически отображаться в пользовательском интерфейсе.

Теперь нам нужно рассказать мнение об этом контроллере и создать привязку. Откройте main_page.js где main_page.js весь код представления, и настройте contentView ниже.

1
2
3
4
5
6
7
8
contentView: SC.ScrollView.design({
  hasHorizontalScroller: NO,
  layout: { top: 150, bottom: 0, left: 0, right: 0 },
  contentView: SC.ListView.design({
    contentBinding: «Microblog.postsController.arrangedObjects»,
    selectionBinding: «Microblog.postsController.selection»
  })
})

Теперь пользовательский интерфейс привязан к содержимому Microblog.postsController . Помните: postsController действует как прокси для его содержимого. В настоящий момент, если мы запускаем приложение, у нас нет набора данных в postsController и, таким образом, SC.ListView по-прежнему пуст. Нам нужно поместить данные в контроллер — мы собираемся сделать это, как только приложение запустится, поэтому откройте main.js и добавьте следующее в основную функцию.

1
2
var posts = Microblog.store.find(Microblog.Posts);
Microblog.postsController.set(«content», posts);

Перезагрузите браузер, и вы должны увидеть что-то похожее на изображение ниже:



Прямо сейчас SC.ListView отображает каждый объект записи в виде строки — не особенно полезной для пользователей. На этом шаге мы собираемся расширить SC.View для предоставления собственного представления / html для каждой строки в SC.ListView .

Сначала откройте терминал и cd в каталог вашего приложения и запустите:

1
sc-gen view Microblog.PostListView

Измените созданный шаблон представления так, как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Microblog.PostListView = SC.View.extend(
  /** @scope Microblog.PostListView.prototype */ {
  publishedDateAsString: function (timestamp) {
    var date = SC.DateTime.create(timestamp),
        now = SC.DateTime.create(),
        dateFormat = «%d/%m»,
        dateStr;
  
    if (SC.DateTime.compareDate(date, now) >= 0) {
        dateStr = «Today»;
    } else {
        dateStr = date.toFormattedString(dateFormat);
    }
  
    return dateStr+» @ «+date.toFormattedString(«%H:%M:%S»);
  },
  render: function (context) {
    var content = this.get(«content»),
        post = content.get(«post»);
  
    var context = context.begin().addClass(«microblog-post»);
    context.push(«<h2>», post, «</h2>»);
  
    context.push(«<span>», this.publishedDateAsString( content.get(«published») * 1000 ), «
    context.end();
  
    sc_super();
  }
});

Метод publishedDateAsString() — это помощник, который сравнивает опубликованную дату с сегодняшней датой и возвращает «Сегодня @ 12:00:00» вместо «17/03 @ 12:00:00». Он использует объект SC.DateTime SproutCore для форматирования и сравнения дат. Посмотрите документацию API для получения дополнительной информации об этом объекте. Функция render — это место, где мы определяем наш пользовательский вид и его HTML. В функцию рендеринга передается context рендеринга представлений. Функция рендеринга вызывается первоначально при отображении представления и после любых последующих обновлений данных. В верхней части функции рендеринга мы получаем объект содержимого этой строки списка и данные публикации. Затем мы начинаем наш контекст визуализации с context.begin() , который определяет новый контейнер div .

Мы также добавляем новый класс CSS в этот microblog-post div используя addClass() . Внутри этого контекста мы добавляем еще два элемента, <h2> который содержит сообщение, и <span> , который использует publishedDateAsString() для форматирования даты публикации. Примечание: мы храним дату в секундах, и, прежде чем мы передадим опубликованную дату в publishedDateAsString() , мы преобразуем ее в миллисекунды. После того, как мы завершили интервалы строки представления одного списка, мы вызываем context.end() , который закрывает контекст рендеринга, и sc_super() для вызова метода render() .

Далее нам нужно указать SC.ListView использовать строку пользовательского представления списка, которую мы определили. Откройте main_page.js и измените contentView ниже:

01
02
03
04
05
06
07
08
09
10
11
12
contentView: SC.ScrollView.design({
  hasHorizontalScroller: NO,
  layout: { top: 150, bottom: 0, left: 0, right: 0 },
  backgroundColor: «white»,
  contentView: SC.ListView.design({
    layout: { left: 200, right: 200 },
    contentBinding: «Microblog.postsController.arrangedObjects»,
    selectionBinding: «Microblog.postsController.selection»,
    exampleView: Microblog.PostListView,
    rowHeight: 60
  })
})

exampleView ссылается на объект пользовательского представления и используется при визуализации строк представления списка. Мы также дали SC.ListView одинаковое правое и левое поле.



Давайте подключим форму так, чтобы при отправке нового сообщения создавался объект. Откройте controllers/posts.js , который содержит Microblog.postsController и добавьте еще один метод, называемый addPost в контроллер, как определено ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
Microblog.postsController = SC.ArrayController.create(
/** @scope Microblog.postsController.prototype */ {
  addPost: function () {
    var postField = Microblog.mainPage.mainPane.get(«contentField»),
        postContent = postField.getFieldValue(),
        published = new Date().getTime() / 1000;
  
    var newPost = Microblog.store.createRecord(Microblog.Posts, {
        post: postContent,
        published: published
    });
  
    postField.setFieldValue(»);
  
    return YES;
  }
}) ;

В этом методе мы используем SC.outlet чтобы получить значение SC.TextFieldView в нашем пользовательском интерфейсе и создать новую запись, используя источник данных SproutCore типа Microblog.Posts . После создания объекта мы сбрасываем значение поля в пустую строку.

Затем откройте main_page.js и подключите кнопку «Post» к методу addPost на контроллере.

1
2
3
4
5
6
7
post: SC.ButtonView.design({
    layout: { top: 80, right: 10, width: 100 },
    title: «Post»,
    isDefault: YES,
    target: «Microblog.postsController»,
    action: «addPost»
})

Мы определяем целевой атрибут как Microblog.postsController , а действие — это новый метод addPost на этом контроллере.

Наконец , чтобы все это работало, нам нужно определить SC.outlet mainPane объекта mainPane чтобы метод addPost мог получить доступ к объекту SC.TextFieldView в представлении. Добавьте следующее свойство в Microblog.mainPage.mainPane , которое определяет выход с именем contentField .

1
contentField: SC.outlet(«postView.form.postContent»),

Окончательный код в main_page.js должен выглядеть так, как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Microblog.mainPage = SC.Page.design({
    mainPane: SC.MainPane.design({
    contentField: SC.outlet(«postView.form.postContent»),
  
    childViews: ‘topView postView contentView’.w(), // SC helper method splits string to array.
  
    topView: SC.ToolbarView.design({
        childViews: «appName».w(),
        layout: { top: 0, left: 0, right: 0, height: 40 },
        anchorLocation: SC.ANCHOR_TOP,
  
        appName: SC.LabelView.design({
            layout: { top: 5, left: 5, width: 100 },
            displayValue: «Microblog App»,
            layerId: «mb-logo» // HTML id attribute
        })
    }),
  
    postView: SC.View.design({
        childViews: ‘form’.w(),
        layout: { top: 40, height: 150 },
        backgroundColor: ‘white’,
  
        form: SC.View.design({
            childViews: ‘postContent post’.w(),
            layout: { left: 200, right: 200 },
  
            postContent: SC.TextFieldView.design({
                layout: { top: 10, left: 10, right: 10, height: 60 },
                isTextArea: YES,
                fieldKey: «content»,
                hint: «What’s on your mind?»
            }),
  
            post: SC.ButtonView.design({
                layout: { top: 80, right: 10, width: 100 },
                title: «Post»,
                isDefault: YES,
                target: «Microblog.postsController»,
                action: «addPost»
            })
        })
    }),
  
    contentView: SC.ScrollView.design({
      hasHorizontalScroller: NO,
      layout: { top: 150, bottom: 0, left: 0, right: 0 },
      backgroundColor: «white»,
      contentView: SC.ListView.design({
        layout: { left: 200, right: 200 },
        contentBinding: «Microblog.postsController.arrangedObjects»,
        selectionBinding: «Microblog.postsController.selection»,
        exampleView: Microblog.PostListView,
        rowHeight: 60
      })
    })
  })
});

После внесения этих изменений перезагрузите браузер и начните добавлять сообщения.



На данный момент данные, которые мы загружаем в наше приложение, поступают из жестко закодированных приборов. Они идеально подходят для тестирования, но не подходят для производства, потому что любые изменения, которые мы вносим, ​​не сохраняются. SproutCore использует SC.DataSource для взаимодействия с хранилищем данных на стороне сервера. По умолчанию он настроен на использование API в соответствии с соглашением REST. На этом этапе мы собираемся расширить SC.DataSource и переопределить ключевые методы, позволяющие нам использовать CouchDB. CouchDB имеет готовый API-интерфейс RESTful, поэтому он идеально подходит для SC.DataSource от SproutCore. Для получения дополнительной информации о CouchDB взгляните на это превосходное введение CouchDB в Nettuts + . Нам нужно всего лишь внести несколько изменений в наш SC.DataSource чтобы позволить SproutCore обрабатывать систему ревизий CouchDB и немного другую схему URL. Давайте начнем с настройки базы данных.

  1. Зайдите на сайт www.couchone.com/get и загрузите CouchDB для вашей операционной системы.
  2. Откройте приложение CouchDB.
  3. Создайте базу данных под названием microblog .
  4. Нажмите на поле выбора с пометкой view и выберите временный вид. Это позволит нам создать представление для отображения всех сообщений.
  5. В функции карты textarea введите нижеприведенную функцию.
    1
    2
    3
    4
    5
    function(doc) {
      if (doc.post && doc.published) {
        emit(doc._id, doc);
      }
    }
  6. Нажмите «Сохранить как» в правом нижнем углу и вызовите как проектный документ, так и просмотр сообщений.

Если что-то из этого незнакомо, я рекомендую вам ознакомиться с введением CouchDB в Nettuts + .

Теперь, когда у нас есть настройка базы данных, мы готовы расширить SC.DataSource . Используя терминал, выполните следующую команду из корневого каталога вашего приложения.

1
sc-gen data-source Microblog.PostsDataSource

Теперь у нас должен быть стандартный SC.DataSource в data_sources/posts.js . Откройте этот файл. Во-первых, нам нужно определить запрос для извлечения наших данных. В верхней части data_sources/posts.js и за пределами конструкции Microblog.PostsDataSource добавьте код, подробно описанный ниже.

1
2
3
4
5
sc_require(‘models/posts’);
  
Microblog.POSTS_QUERY = SC.Query.local(Microblog.Posts, {
  orderBy: ‘published DESC’
});

Это определяет пользовательский запрос, который гарантирует, что мы получим все сообщения и упорядочим их по дате публикации. Затем добавьте некоторые вспомогательные методы в подкласс SC.DataSource как определено ниже.

1
2
3
4
5
6
7
8
9
// DB name
_dbname: «microblog»,
  
getPath: function (resource) {
    return «/»+this._dbname+»//»+resource;
},
getView: function (view, queryString) {
    return «/»+this._dbname+»/_design/»+view+»/_view/»+view+»/?»+queryString;
},

Как видите, мы сохраняем имя БД в «закрытой» переменной. Вы можете добавить закрытие, если хотите быть тщательным. Функции getPath() и getView() абстрагируют ресурс от сопоставления пути. Мы можем вызвать getView() с любым именем представления и получить правильный URL для этого ресурса в экземпляре CouchDB.

Теперь давайте реализуем несколько методов для извлечения сообщений из CouchDB. didFetchPosts() метод fetch() и добавьте didFetchPosts() как описано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fetch: function(store, query) {
if (query === Microblog.POSTS_QUERY) {
  SC.Request.getUrl(this.getView(«posts», «descending=true»)).json()
            .header(«Accept», «application/json»)
            .notify(this, «didFetchPosts», store, query)
            .send();
  
  return YES;
}
  
return NO;
},
  
didFetchPosts: function(res, store, query) {
    if (SC.ok(res)) {
      var couchResponse = SC.json.decode( res.get(‘encodedBody’) ),
          posts = couchResponse.rows.getEach(«value»)
          ids = couchResponse.rows.getEach(«key»);
  
      store.loadRecords(Microblog.Posts, posts, ids);
      store.dataSourceDidFetchQuery(query);
    } else {
  store.dataSourceDidErrorQuery(query, res);
    }
},

Метод fetch() гарантирует, что запрос поддерживается, а затем, используя SC.Request , делает запрос к CouchDB для представления сообщений, которое мы указали ранее. После выполнения запроса мы просим SproutCore уведомить метод didFetchPosts() о результате. В рамках didFetchPosts() мы сначала проверяем, что ответ прошел нормально. Если это не так, мы отвечаем с ошибкой, соответственно. С другой стороны, если ответ был успешным, мы перебираем результаты, получаем все сообщения и идентификаторы, а затем загружаем записи. После загрузки записей мы уведомляем всех слушателей, что источник данных получил некоторые результаты. При этом это автоматически обновляет наш пользовательский интерфейс из-за привязки данных, которую мы изначально настроили.

Далее нам нужно обработать новое сообщение, убедившись, что мы добавили сообщение в CouchDB. Снова в Microblog.PostsDataSource добавьте следующие методы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
createRecord: function(store, storeKey) {
  // Check we want to store the correct type of record.
  if (SC.kindOf(store.recordTypeFor(storeKey), Microblog.Posts)) {
    SC.Request.postUrl(this.getPath(«/»)).json()
              .header(«Accept», «application/json»)
              .notify(this, this.didCreatePost, store, storeKey)
              .send(store.readDataHash(storeKey));
 
    return YES;
  }
 
  return NO ;
},
 
didCreatePost: function (res, store, key) {
  var couchResponse = this.handleCouchResponse(res);
  if (couchResponse.ok) {
    // Copy CouchDB _id and _rev into Microblog object/document.
    var doc = store.readDataHash(key);
    doc._id = couchResponse.id;
    doc._rev = couchResponse.rev;
 
    store.dataSourceDidComplete(key, doc, doc._id);
  } else {
    store.dataSourceDidError(key, res);
  }
},
 
handleCouchResponse: function (res) {
  if (SC.ok(res)) {
    var couch = SC.json.decode( res.get(«encodedBody») );
    if (couch.ok == YES) {
      return { «ok»: true, «id»: couch.id, «rev»: couch.rev };
    } else {
      return { «error»: true, «response»: res };
    }
  }
 
  return { «error»: true, «reponse»: «Unkown error occurred.»
},

createMethod() сначала гарантирует, что createMethod() нам передали запись правильного типа. Затем он отправляет запрос POST в CouchDB с объектом post в качестве данных. Еще раз, мы прилагаем метод, который будет уведомлен, когда запрос закончен. didCreatePost() использует вспомогательный метод handleCouchResponse() , который сначала проверяет, нашел ли SproutCore ответ OK, а затем опрашивает возвращенные данные, чтобы убедиться, что вставка была успешно выполнена CouchDB. Если все прошло правильно, мы возвращаем объект с новым идентификатором документа и номером редакции. didCreatePost() продолжает выполняться, если документ был успешно создан. Если это не удалось, мы уведомляем всех слушателей о сбое. Затем метод копирует идентификатор и номер редакции в локальный документ в SproutCore и уведомляет всех слушателей о новом документе. Еще раз, пользовательский интерфейс автоматически обновится, как только новый документ будет вставлен в массив записей.

Далее нам нужно изменить файл main.js чтобы использовать новый запрос загрузки, как main.js ниже.

1
2
var posts = Microblog.store.find(Microblog.POSTS_QUERY);
Microblog.postsController.set(«content», posts);

И измените core.js чтобы использовать правильный объект источника данных.

01
02
03
04
05
06
07
08
09
10
Microblog = SC.Application.create(
  /** @scope Microblog.prototype */ {
  
  NAMESPACE: ‘Microblog’,
  VERSION: ‘0.1.0’,
  
  store: SC.Store.create({
    commitRecordsAutomatically: YES
  }).from(«Microblog.PostsDataSource»)
}) ;

Наконец , мы должны добавить прокси-сервер к SproutCore BuildFile в главном каталоге приложения, сообщив SproutCore искать CouchDB на другом порту. Добавьте приведенный ниже прокси в ваш BuildFile

1
proxy ‘/microblog’, :to => ‘localhost:5984’

И перезапустите сервер SproutCore.

Если вы перезагрузите свой браузер, вы не должны видеть никаких сообщений. Это имеет смысл, поскольку их еще нет в базе данных CouchDB. Сделайте сообщение, перезагрузите браузер, и вы все равно должны увидеть его. Вы также можете проверить с помощью Futon, панели администрирования CouchDB, чтобы увидеть полученные документы в базе данных.



В этом уроке я познакомил вас с некоторыми ключевыми понятиями в SproutCore и создал простое приложение для микроблогов, похожее на Twitter. Наконец, мы в полной мере воспользовались клиентской структурой SproutCore и интерфейсом RESTful CouchDB для создания полнофункционального настольного приложения с сохранением данных без уровня приложения на стороне сервера. Большое спасибо за чтение!