Placeholder Image

字幕列表 影片播放

  • BRAD GREEN: Good morning, everyone.

  • I'm Brad Green.

  • I'm the manager of the Angular project here at Google.

  • And I'm joined by John and Darrel from TiKL.

  • And they're going to talk to us about building

  • Chrome Apps with AngularJS.

  • And I'm sorry, you guys.

  • For this holiday addition, John's

  • actually wearing some spectacular Christmas pants,

  • but you can't see it on camera.

  • Maybe at the end, we can get a shot of that.

  • So hey guys, tell us a little bit

  • about what TiKL does before we get

  • into the presentation about building apps.

  • DARREL SUMI: Sure.

  • JOHN FEIG: Sure, yeah.

  • We're mainly a mobile app company.

  • We have several communications apps.

  • We have three on Android.

  • And wait-- no, we have four on Android.

  • DARREL SUMI: Four on Android, two on iOS.

  • JOHN FEIG: Two on iOS.

  • So we've got TiKL, which is like a walkie-talkie.

  • It works just like a walkie-talkie.

  • It's pretty fun.

  • We have Talkray, which is our main focus these days,

  • which is a unified communications app.

  • You do texting, calling-- it does groups up to 25 people.

  • KeeChat, which is what we're going

  • to be talking about today, which is

  • group texting with usernames.

  • And then we have a new app out that we launched a couple weeks

  • ago, which is a PTT recorded app,

  • so you can send voice mails back and forth.

  • It's called VoMessenger.

  • BRAD GREEN: So you guys have been primarily doing Java and--

  • DARREL SUMI: Yes.

  • BRAD GREEN: --maybe Objective-C development.

  • And you've just recently come into the web

  • way of developing things.

  • And so how long have you been doing this?

  • DARREL SUMI: Since we started as a company,

  • it's probably-- I don't know, five months or so where

  • we've been kind of jumped into this.

  • JOHN FEIG: Yeah, we started over the summer doing the web stuff.

  • DARREL SUMI: Yeah.

  • I mean, both of us have obviously

  • dabbled in a lot of web stuff before.

  • But this is our first real, larger project.

  • JOHN FEIG: Yeah, right.

  • Definitely the first big project.

  • And the company, as a company, doesn't really

  • have much experience with web things.

  • TiKL was started in 2010.

  • They had the TiKL app for a while.

  • And then we jumped into Talkray.

  • And that was iOS and Android.

  • And we've basically been focused on mobile exclusively

  • until this product.

  • BRAD GREEN: OK, very cool.

  • All right.

  • Well, then, let's jump into the presentation.

  • And tell us about what you guys have

  • learned about the environment.

  • JOHN FEIG: Great.

  • So we can start with the slides.

  • So this is KeeChat.

  • DARREL SUMI: Yeah.

  • So these are just a couple screenshots

  • of what the mobile app looks like.

  • So basically, like what we said, it's a unified messaging app.

  • So you can send regular texting images to anyone

  • within our system.

  • JOHN FEIG: And we recently built a Chrome App out of it.

  • We started with a web version of this.

  • And then we sort of transitioned to a Chrome App.

  • And we'll talk a little bit about why

  • we picked Angular and Chrome.

  • DARREL SUMI: Yeah.

  • Angular really fit with, like we said, our background in Java.

  • We wanted the look and feel of the entire thing

  • to be very much like an app, less a traditional web

  • style of doing things.

  • And everything, like the MVC architecture of Angular,

  • really fit into the pre-existing model

  • that we had built everything around on the mobile side.

  • JOHN FEIG: Right.

  • Yeah.

  • It was very comfortable for us to look at

  • because it just felt a lot like being able to build an app.

  • And you can see here the structure

  • that we chose for KeeChat, which is basically

  • Angular's suggested structure for especially large projects.

  • So we broke it out into controllers and services

  • and models are the main components here,

  • trying to pick apart with the MVC architecture

  • and do this properly.

  • And it was really nice to have services.

  • For us, coming from Java, where they're sort of like

  • singletons.

  • They work in a similar way.

  • There's a lot of nice stuff.

  • And organizing your code like this

  • really helps in larger projects.

  • DARREL SUMI: Yes.

  • So I guess one of the reasons, or one of the things,

  • is why did we chose to do a Chrome App off the Angular app.

  • Why not just keep it a website kind of thing?

  • So obviously, the biggest one is the Chrome Web Store.

  • That's a great way to get distribution for our app.

  • The other one is that, like I said,

  • we really wanted to make it feel like an app.

  • We wanted users who came from our mobile

  • to be able to experience the same kind of experience

  • that we provided on their computers.

  • So one of the big things is notifications.

  • That makes it feel like a much more rich experience.

  • And obviously, raw sockets were pretty important for us

  • because we're a communication app.

  • JOHN FEIG: Yeah.

  • We had played around with a couple different socket clients

  • and decided that for us to get out the door,

  • raw sockets was going to be the easiest way for us to do it.

  • So that was something that was available in Chrome Apps

  • that really you can't do in HTML5.

  • There's WebSockets, but it's not the same thing.

  • Java can use WebSockets in Java 7,

  • but we're not running Java 7 on the server.

  • DARREL SUMI: So some of the tools

  • we use are-- we'll just briefly go over this-- Yeoman, Grunt,

  • Bower, and Karma.

  • Karma's our testing suite.

  • The other three really help us organize our code a lot,

  • bring in all the dependencies, keep everything clean.

  • JOHN FEIG: Yeah.

  • It's pretty standard stuff.

  • And it was really nice with Grunt, actually.

  • You can run Grunt server.

  • And it has listeners for things.

  • And so it was really funny.

  • Because when we were really running through this stuff,

  • it was faster developing on the web than it was for Android.

  • Because you'd change something, and then your browser

  • would get updated right away.

  • I don't know.

  • It was kind of funny.

  • DARREL SUMI: Yeah, we're just used to waiting, compiling,

  • and doing all this stuff for Android development.

  • JOHN FEIG: Yeah.

  • And we didn't even have to hit Refresh on the browser window

  • here.

  • So it's kind of nice.

  • DARREL SUMI: So one of the main concerns, when you're

  • thinking about building a Chrome App,

  • is what are all the dependencies in your project

  • that you're going to need to re-work around the Chrome Apps.

  • And one of them, the big one, was RequireJS.

  • So we used Require a lot.

  • It's a great modularization tool that you can use-- or library.

  • A lot of people use it.

  • It's a very common thing.

  • But they kind of conflict a little bit

  • with the module system in Angular.

  • They don't necessarily directly-- they're

  • not against each other.

  • In fact, that they're orthogonal ideas of the way

  • they treat modularization.

  • But they are going to step over each other a little bit,

  • so you've got to understand if you really need it

  • and how to deal with it.

  • JOHN FEIG: For us, we had all these third-party libraries

  • that we just had to use.

  • And some of those required Require.

  • So it's like, OK, we're stuck using Require.

  • It's kind of painful to get implemented properly

  • with Angular.

  • If you can avoid it, it's probably better to avoid it,

  • but--

  • DARREL SUMI: But it's still a great tool.

  • And if you need it, there are several resources

  • online that show examples of how exactly to do it.

  • So it's not-- there's ways around it.

  • JOHN FEIG: And I have read that Angular

  • is supposed to be getting better about trying

  • to integrate with Require.

  • BRAD GREEN: We do want to work on that more.

  • So like you say, these are two orthogonal things.

  • One is about what does my JavaScript class need in order

  • to run.

  • The other is, what order should I load vals.

  • DARREL SUMI: Right.

  • Dependency order and then lazy loading, and things like that.

  • BRAD GREEN: It would be nice if these worked well together.

  • This is some future thing we'd like to get right.

  • JOHN FEIG: So, yeah.

  • I guess now we're just going to dive

  • into what the process of converting a web Angular

  • app to a Chrome App was.

  • DARREL SUMI: So just kind of the quick few basics

  • that every kind of Chrome App needs

  • is that it's going to need a manifest.json file that pretty

  • much describes all the permissions and the app name

  • and things that go into the store.

  • There's going to be a background.js file that

  • describes several entry points into the app,

  • like what happens when you install

  • the app, what happens on launch.

  • And you're going to need here, obviously, your index files,

  • your main index file.

  • But there's a trick to this one is that it sometimes

  • differs from what you normally would have in your Angular

  • project.

  • JOHN FEIG: OK.

  • So we can look at the manifest here for a little demo app

  • that I did.

  • And it's really pretty basic.

  • You've got your name, description.

  • You have the entry point of the JavaScript for your app

  • to find, which is background.js.

  • That's a pretty standard thing within Chrome Apps.

  • And then permissions-- so for example,

  • I need to be able to talk to the ajax.googleapis.com site.

  • And I also need to have storage permissions.

  • And then you also specify your icons.

  • DARREL SUMI: Yeah, pretty straightforward.

  • And like I said, for the background,

  • you can pretty much describe generic things

  • about your Chrome App.

  • So in this case, all we said is the width is going to be 1,200,

  • the height's going to be 700.

  • JOHN FEIG: Yeah.

  • And then your index is chomeindex.html.

  • And you can name it whatever you want.

  • But I think it's easy just to call it Chrome Index.

  • And your regular one's Index.

  • OK.

  • DARREL SUMI: So once you add all these things in,

  • and you think, OK, this is going to-- things

  • are going to look good.

  • And you try running it.

  • Of course, nothing works, right?

  • JOHN FEIG: Yeah.

  • You get a lot of red.

  • DARREL SUMI: Right, right.

  • And so one of the main, first ways to approach this

  • is there's a thing called a Content Security

  • Policy that web browsers implement.

  • It basically restricts what you can

  • do so that third-party external code doesn't maliciously

  • run some hacks on your website and cause

  • all kinds of problems.

  • So this is something that's already

  • out in most web browsers.

  • But then Chrome Apps have a tighter restriction

  • on this Content Security Policy.

  • So the three main things that differ from traditional web are

  • is that it's going to restrict any inline scripting.

  • It's going to restrict any external resources.

  • So if you link to some extra library somewhere else,

  • you can't necessarily just link to that

  • to bring that script in.

  • And it's going to restrict anything

  • that pretty much converts strings into JavaScript.

  • So things that use the eval or new Function methods

  • are not going to work in Chrome Apps.

  • So the second thing that I talked about,

  • the external resources, so the first thing

  • you're going to look at is what third-party libraries

  • you bring in that you might actually

  • reference from a CDN or something.

  • These you can't bring into your app directly,

  • so you're going to have to download

  • these libraries or any styles, or fonts, images,

  • any other kind of static content that you might

  • be able to just bring into your app and have it

  • hosted straight from there.

  • And so you're going to have to modify your index

  • file, obviously, because that's where all these things get

  • referenced.

  • So here's a quick example of how the index that you might have

  • for your regular Angular projects

  • will differ from the Chrome one.

  • So first one, when we reference the app,

  • is going to be a ngCsp.

  • JOHN FEIG: I don't think it highlights.

  • DARREL SUMI: Yeah, it doesn't highlight.

  • There's going to be a ngCsp Angular

  • tag that you're going to put there.

  • What this describes is that it tells

  • Angular to be in a CSP mode, essentially.

  • Angular has a lot of optimizations

  • that use things like the eval and new Function,

  • I believe, basically just to optimize Angular.

  • But for it to run in the Chrome App,

  • you have to restrict those kinds of things.

  • So it has a CSP mode that'll get around that.

  • BRAD GREEN: It's actually only a few little things.

  • You don't lose very much at all.

  • DARREL SUMI: OK.

  • Well, yeah.

  • Great.

  • BRAD GREEN: It's one small optimization.

  • But yes, you do lose it.

  • DARREL SUMI: Right, right.

  • And so you do need to specify that.

  • And obviously, now that you downloaded-- say,

  • in this example it's a Socket.IO.

  • We were referencing it from the CDN.

  • And so now we just have to bring it into your own project

  • and reference it that way.

  • Pretty straightforward.

  • JOHN FEIG: Yep.

  • And then you try running it again, and it's still broken.

  • So--

  • DARREL SUMI: OK, so here's a little demo-- let me switch.

  • Here's a little demo of a socket app that I made.

  • So basically, all this is-- it's just a small little test

  • demo that connects to a Node.js server.

  • The Node.js server starts counting

  • every one, two, three-- every second.

  • And it would reply back on Socket.IO

  • to the client and display what the current count is,

  • essentially.

  • Really simple-- that's all that's happening.

  • And so here, you'll see that the counter's incrementing.

  • But at this point, the Angular project

  • doesn't know anything about what the count was previously.

  • It doesn't know anything about who its friends are.

  • These are all initialized and given to us by the Node server.

  • JOHN FEIG: Yeah.

  • You don't want to dive into-- OK.

  • DARREL SUMI: Whoa.

  • Whoops.

  • There we go.

  • OK.

  • So one of the key components to Angular as well as Chrome Apps

  • is that the way you think about web servers

  • traditionally is switched to be using an API server.

  • So the web server you need to treat as an API server.

  • And what that means is that the web server is not

  • going to hand down HTML or things like that, where it's

  • traditionally-- they might have injected information, say.

  • You grab information from your database,

  • and you put it in your friends list or something, for example.

  • And it's not going to inject [? a string ?] into the HTML

  • and hand you back the HTML.

  • Rather, all that's going to exist in your Angular app

  • already.

  • And really, what your server should be doing

  • is sending back just the data, none of the UI components.

  • So in this example, this is how the HTML

  • looks like for the example I just showed you.

  • Pretty much all we know on the Angular side

  • is that there's going to be a count.

  • There's going to be a count listed here.

  • Whoops.

  • That doesn't do that.

  • JOHN FEIG: [LAUGHTER] No.

  • DARREL SUMI: OK.

  • So there's going to be a count referenced there.

  • And we're going to be repeating over a list of friends.

  • But we don't know who the friends are at this point.

  • There might be one friend.

  • There might be hundreds.

  • And we don't know.

  • But really, you need to treat your web server

  • to give you those things as an API server.

  • BRAD GREEN: And this is the great thing about Angular

  • is you're going to build your app this way anyway.

  • So there's not much in the transition.

  • JOHN FEIG: Right.

  • DARREL SUMI: Exactly.

  • JOHN FEIG: So we're going to look at Socket.IO here.

  • DARREL SUMI: OK.

  • JOHN FEIG: Whoops.

  • Uh-oh.

  • That's not good.

  • DARREL SUMI: OK.

  • So here is some of the code for this example.

  • And this is all, by the way, {public} GitHub {repress},

  • so that you can look it later on your time.

  • JOHN FEIG: Yeah.

  • We have links at the end.

  • DARREL SUMI: Right, right.

  • So this is the main controller for the example I was just

  • [? saying ?].

  • So basically, there is going to be a count that's

  • initialized at zero and an empty friends list.

  • Like I said, you don't really know anything

  • about what's happening.

  • And the way we have it structured

  • is that initially, you just connect to the server.

  • The server is, like I said, a Node.js server

  • that's running locally.

  • And here is a service that controls the communication

  • to that Node server.

  • So in server connect, where you start off,

  • essentially we just connect to-- I have a hard-coded thing

  • to look a host right here.

  • And at this point, we don't know any kind

  • of data that's been passed through.

  • We just know that we started with nothing.

  • So what we do is we tell it to connect.

  • And when we connect, we event an emit back to our controller,

  • saying, hey, we've connected.

  • And at this point--

  • BRAD GREEN: Actually, Darrel, maybe just bump the font

  • up a point or two.

  • DARREL SUMI: Sure.

  • What was it?

  • Command-Plus?

  • JOHN FEIG: Yeah.

  • DARREL SUMI: There we go.

  • BRAD GREEN: That'll be good.

  • JOHN FEIG: Sorry about that.

  • DARREL SUMI: So in our controller,

  • when you do connect, we're going to tell it to initialize,

  • initialize our state.

  • So at this point, your API server

  • doesn't really know anything about where you've left off

  • or what it is.

  • It's just going to start counting from some random--

  • from zero, if the server doesn't know anything.

  • So it helps if you pass in some kind of initialization data

  • to tell it, hey, this is where we left off.

  • And this leads into a key point of how

  • you want to treat offline and online mode.

  • But we'll get to that in a second.

  • And so we initialize it with our current count,

  • which at this point is zero.

  • And when we get a response back from the server here,

  • and it responds here, we'll get back some data.

  • And in the data-- like I just know

  • it's going to contain the current count and just

  • a list of friends, which doesn't really change.

  • And then from that point onward, the socket's

  • going to connect-- it's going to listen for a counter update

  • event that's going to get pushed down once

  • every second from the Node server.

  • And it's just going emit the count right here.

  • So the count is actually incremented on the server side.

  • So all that kind of logic-- obviously,

  • this is a contrived example of what

  • you might want to pass down, what information you

  • want to pass down from your server.

  • But essentially, the server's going

  • to be handling all that kind of data management,

  • a lot of that stuff that goes on.

  • But you'll notice here that this is all just

  • standard Socket.IO stuff if you have worked with it before.

  • [INAUDIBLE] All right.

  • JOHN FEIG: There we go.

  • DARREL SUMI: OK.

  • Right.

  • And so one of the things that you

  • have to consider when you build your Chrome app,

  • and for Angular as well, is how this

  • is going to get treated in an offline mode

  • or in a state where you're not sure about the connectivity

  • of things.

  • So you do need to handle things like disconnections or crappy

  • connections.

  • And a lot of that needs to go into any kind of socket code

  • that you write.

  • So luckily, Socket.IO handles a lot

  • of the disconnect and auto-reconnect logic.

  • But if you're working with raw sockets,

  • you're going to need to handle these kinds of things, as well.

  • But more than just the actual connectivity,

  • you need to think about how your app is going

  • to work from the user's standpoint

  • when you don't have connectivity.

  • If you start up the app, will you be showing a blank page?

  • If you lose connection in the middle, what's

  • going to happen to, let's say, in our example,

  • the counter that you have going on?

  • JOHN FEIG: Right.

  • You don't want to just throw up a Refresh button

  • and make the user think about that stuff.

  • You want to handle it in a smart way

  • so that it's as automatic as it can be.

  • DARREL SUMI: Right, right.

  • So in our example-- can we go-- what's it called?

  • Terminate the server.

  • JOHN FEIG: Oh, the-- OK.

  • Um, yeah.

  • There we go.

  • OK.

  • Server's up there.

  • DARREL SUMI: OK.

  • So we just--

  • JOHN FEIG: Server's dead.

  • DARREL SUMI: Yeah, killed our server, essentially.

  • And when you go back to this, you'll

  • notice that the counter has stopped.

  • You'll probably want to do something

  • to signify that you're in an offline mode, maybe.

  • That might help, so the user doesn't

  • get confused, like, why isn't anything working?

  • But you notice that the count is just stopped at here.

  • But if you were to redo the server,

  • or reconnect it, after some retrial logic,

  • it's going to essentially start back

  • from where we previously were counting.

  • So you don't want to necessarily start from scratch

  • every time you reconnect.

  • You want to leave the user off where they were before

  • and [? smoothen ?] the transition

  • between these things.

  • JOHN FEIG: You can see it worked.

  • We were stuck at 419, and now we're counting again.

  • DARREL SUMI: Right, right.

  • So you don't necessarily want to start at zero every time.

  • JOHN FEIG: OK.

  • So go here.

  • DARREL SUMI: So this whole example

  • was, [? obviously, ?] just a regular Angular app.

  • And the same thing works for--

  • JOHN FEIG: Kill this one.

  • DARREL SUMI: Yeah.

  • Same thing works-- the transition

  • from bringing Socket.IO into Chrome Apps

  • is really, really simple, pretty much like what

  • we showed earlier where you reference the Socket.IO, not

  • from CDN but from your own project.

  • After you do that, that's pretty much it.

  • There isn't a whole lot of things

  • that you need to do to get Socket.IO working.

  • It actually just works out of the box.

  • JOHN FEIG: Yeah, which was really surprising to me

  • because I thought that this might be restricted

  • or it might be really difficult.

  • But Socket.IO just works.

  • Where do you want to go?

  • DARREL SUMI: Yeah, there we go.

  • And so on top of Socket.IO, Chrome Apps

  • provides a really great Sockets API.

  • It gives you access to raw TCP and UDP sockets.

  • It's through the chrome.socket API.

  • I have an example on the GitHub that is the same exact demo

  • that we just showed but just rewritten for TCP.

  • So if you want to look at that, here's a link for that.

  • But we necessarily won't go into that code.

  • JOHN FEIG: It's a little bit more verbose,

  • but it's also a lot more powerful.

  • Like I mentioned at the top, you get access

  • to real raw sockets with this, which, like I said,

  • we just needed for our app.

  • BRAD GREEN: Just so folks can think about,

  • which one would I need?

  • What did you guys need the raw sockets for?

  • JOHN FEIG: So we're sending binary data back and forth

  • to the server.

  • Socket.IO sends back and forth strings.

  • So if you want to send that sort of data over Socket.IO,

  • you have to encode it as a string first.

  • And then you have to decode it on the server side.

  • You also have to have server-side components that

  • can handle Socket.IO.

  • Depending on the server that you're running the sockets,

  • you might have a good Socket.IO library available to you.

  • Like if you're running Node, it's really easy

  • to just pull in Socket.IO.

  • But if you're running Java, there's

  • not really a great solution for Socket.IO.

  • So it's a lot easier to use raw sockets there.

  • DARREL SUMI: It just depends on what your current architecture

  • is and structures.

  • For us, since we came from a mobile app

  • with a pre-existing system, it was easier to plug into that

  • rather than recreate everything on a different system.

  • JOHN FEIG: Right.

  • And there's also WebSockets, too, like we mentioned.

  • And those are maybe a little bit more available on servers.

  • So it just depends on what your needs are.

  • But it's really nice to have Chrome sockets available.

  • So the next thing that you want to think about,

  • in terms of how your app behaves when it's offline,

  • is your local storage stuff.

  • And this is actually something that I

  • started with in my demo, which is a little podcast app,

  • before I even thought about converting this into a Chrome

  • App.

  • The main reason is because I wanted

  • it to behave like an app.

  • I wanted people to be able to subscribe to podcasts

  • and then listen to episodes.

  • I wanted it to keep track of how much they

  • had listened to so far.

  • One thing that really drives me nuts is I

  • have a couple tools that I use, aside from my app,

  • for listening to podcasts, and they always

  • forget where I left off.

  • And some of the podcasts I listen to

  • are multiple hours long.

  • And if the thing goes down or you accidentally click Refresh,

  • and you start back at zero when you're an hour and a half

  • in, that's not too fun .

  • So this stuff is really important.

  • It's really useful.

  • And storing stuff on the client side

  • in certain cases-- like for KeeChat for example,

  • all of your chats are stored locally-- your contacts list.

  • You want all that stuff local.

  • Especially if it's just going to be for you,

  • there's no reason to have it on the server.

  • So there are some slight differences

  • between how Chrome handles local storage versus how

  • HTML5 handles local storage.

  • And I will show you right here.

  • So the big difference is that HTML5 is synchronous.

  • It has a synchronous API for local storage, which

  • means that you can just say, myVal equals localStorage key.

  • So that's just getting the thing right off a disk.

  • It blocks in-line.

  • And so you're going to be blocking on whatever thread

  • you're calling that on.

  • And Chrome is asynchronous.

  • So it's going to go out to disk and then

  • return in a callback the thing.

  • And you can just follow along here.

  • I highlighted in blue the value that I'm trying to set.

  • And then orange here is the value

  • that's coming back from disk.

  • So in the HTML5 example at the top,

  • that's just one line that we have to call.

  • But again, it blocks there.

  • And then the example below is a little bit more verbose,

  • but you can see we start out with null set to that.

  • And then we define a callback function

  • that's going to take a single parameter

  • and assign it to the value that we want set.

  • Oh, boy.

  • Ha.

  • There we go.

  • And then we do this chrome.storage.local.get.

  • And then the first parameter is the key,

  • which is the same key that we have up above.

  • And then whatever gets returned is

  • going to get passed to a function.

  • And it's going to be callback and then data, key.

  • So you get an object back.

  • And you have to pull the value out of the object.

  • It's really not too tough.

  • It's just slightly different because it's asynchronous.

  • And the big deal here is that there

  • may be some third-party libraries, especially if you're

  • in a larger project, that make assumptions

  • about having the synchronous API for local storage.

  • So there was one library that we ran

  • into that was a pretty integral part of KeeChat that just made

  • assumptions all over the place about local storage

  • being synchronous.

  • And we made assumptions-- we kind of used their API

  • in a synchronous way because those were synchronous calls.

  • So it made sense to use it in a synchronous way.

  • So what happened was when we did this change to Chrome,

  • we had to go through and rewrite quite a bit

  • of that third-party library to make

  • it function asynchronously.

  • That was one of the more painful things.

  • It wasn't a huge deal.

  • And it wasn't technically difficult.

  • It was just annoying because you have this dependency.

  • And it's not working for you.

  • So that stuff you might run into.

  • But--

  • DARREL SUMI: So pretty much, you always want

  • to check and see what kind of assumptions

  • any third-party banks on the access to the APIs,

  • as well as the thing like we talked

  • about earlier-- the Content Security Policy.

  • A lot of these libraries are going to make assumptions,

  • a lot of them more towards the way web

  • has been done up till now and not the Angular

  • way or the Chrome App way.

  • JOHN FEIG: And another great way to persist data locally

  • is with IndexedDB, which is an HTML5 API.

  • And it can actually be a much better fit

  • for certain types of data.

  • So if you have larger data sets, anything that's not a string,

  • really, you probably want to use IndexedDB.

  • It's a NoSQL database on a client.

  • The API's a little-- it takes a little getting used to.

  • It's a little verbose.

  • I actually wrote a wrapper to help

  • with doing things with Angular.

  • So it's an AngularJS IndexedDB wrapper

  • so you can just inject it.

  • And then you just do much simpler calls.

  • But here's an example of IndexedDB Put.

  • So you have a datastore.

  • You have a database.

  • The storeName is kind of like a table.

  • So it's like-- I mean, it's a datastore for NoSQL.

  • And we're just going to put some data, which is an object,

  • so it's going to be key value pairs in that object.

  • And so you have a reference to the database already.

  • You have to have opened the database.

  • And there's a whole process for that.

  • And then you get a transaction.

  • With the storeName, it has to be in read-write mode

  • if you're going to be persisting things.

  • I'm going to say objectStore, dot put, and then your object.

  • And then, hopefully, you'll go through this on,

  • success callback that has your event.

  • And now what I'm doing here is I'm

  • emitting an event through Angular, through the Angular

  • scope, that this data was put, and then on what database,

  • and what storeName.

  • And then I have a similar event in the error case.

  • DARREL SUMI: And this whole wrapper

  • is put as a Angular service, right?

  • JOHN FEIG: Mm-hm.

  • Yep.

  • So it's a singleton.

  • You can call it when you set up your app.

  • I tried to make it as simple as possible.

  • And we actually used the same library in KeeChat.

  • It cut way down on the amount of code

  • that I had to write over trying to do things

  • with local storage.

  • BRAD GREEN: And you'll be able to share that link with us?

  • DARREL SUMI: Yes.

  • JOHN FEIG: Yep, yep.

  • It's at the end.

  • BRAD GREEN: Great.

  • JOHN FEIG: So I actually also have a wrapper

  • for the local storage to choose between Chrome versus the HTML5

  • thing.

  • It tests to see if chrome.storage exists.

  • And if it does, it uses that.

  • And you can also set it up to use

  • the sync, chrome.storage.sync versus local,

  • which will sync any of those strings

  • across multiple instances of Chrome.

  • So if you're logged into Chrome on your laptop at home

  • and your laptop at work, you can actually

  • have that stuff synced up, which is nice.

  • So back to the slide.

  • Angular has-- it's just an aside,

  • but Angular has these really nice event emitters.

  • In KeeChat, we used a third-party event emitter

  • library because we had-- like I mentioned at the top,

  • there's some third-party libraries

  • that make assumptions about what's available.

  • One of those assumptions was that event emitters

  • would be available.

  • In my podcast demo, I didn't need that.

  • I didn't really use much third-party stuff.

  • So I could use the Angular event emitters,

  • which are really nice because this is done on the scope,

  • on Angular's scope.

  • So Angular knows about these things.

  • You don't have to run scope.apply

  • to get the digest cycle going.

  • So when you send a controller an event, it's going to show up.

  • So it's a really simple API.

  • You listen for-- you register for events with scope.on.

  • You say, like, i_am_event is the string that's

  • going to identify this.

  • And then the second parameter is a function

  • with two parameters, event and data.

  • The event I never use at all.

  • But data is important.

  • And so then you can emit it with rootScope.emit.

  • And it's rootScope because you're usually

  • going to be doing this from a service.

  • Services can't get scope injected.

  • And they probably wouldn't be on the same scope anyway.

  • So rootScope is what you use.

  • DARREL SUMI: The syntax-- the whole notion of this

  • is fairly common, standard event emitter type of paradigm

  • that people are probably used to for any other system.

  • It's just that these events get passed through the scope,

  • so you do have to understand how it goes up and down the scope.

  • JOHN FEIG: Right.

  • Yeah.

  • There are a couple options here.

  • So it's depending on where you're firing events from

  • and where you want to get them to, you might need emit

  • or you might need broadcast.

  • And they take the same parameters.

  • But broadcast basically sends things down to children.

  • Emit goes up the stack.

  • So you have to know which one you need.

  • But yeah, event emitters are really useful.

  • And they really help clean up code.

  • And you don't have to have as many things explicitly

  • calling one another.

  • DARREL SUMI: It just really helps

  • keep your code structured in a really nice, clean way

  • and modularized that way.

  • JOHN FEIG: Right Yeah.

  • It really cuts down on interdependency stuff.

  • And then I have just one other quick example of IndexedDB Get.

  • So very similar to Put, this is just

  • getting some item out of the database.

  • This time, we're going to just use

  • a key, which can be a string, whatever the type for your key

  • is.

  • And so we have a similar thing where we get the transaction

  • out of the database with the storeName.

  • You don't need to be in read-write mode.

  • You don't need to pass the mode in.

  • The default is just read.

  • And then the onsuccess case again,

  • we're emitting something with Angular's rootScope emitter.

  • We say, we've gotten this item.

  • And then we're going to pass an array of results.

  • So this is nice.

  • With IndexedDB, I like to always pass back

  • the database name, the storeName, and then the data so

  • that in my controller-- whatever is getting this data, if it's

  • a service controller or whatever, I can first

  • check to see-- OK, getitem is pretty generic, right?

  • And since this is a service library that I wrote,

  • I'm not going to have dozens of different names

  • for these things.

  • If I have common names, and I pass back

  • identifiers in the data, it just helps

  • so that I can register the listener with that common name

  • and then just inspect things that come back on those event

  • listeners when I get them.

  • So after you do your local storage, convert things

  • to IndexedDB, it's still not going to work.

  • You probably have some dynamic content

  • that you need to handle.

  • DARREL SUMI: And this goes back to the notion of the CSP

  • that you can't bring in external content

  • and just think it's going to run all fine and dandy.

  • JOHN FEIG: Right.

  • So actually, this would probably be a good time

  • to explain one thing here.

  • So this is my little podcast app that I did.

  • And this is actually the Chrome App version.

  • So you can see, I launched it there.

  • And so this middle section here you can see

  • is all pulled in with a web request.

  • So this side-- the Add Podcast and List

  • a Podcast side-- that's local.

  • And then I have this section up top.

  • This-- you've got a playlist here on the left.

  • That's local.

  • But then the content with text that

  • pulls in information about either the show or all

  • these episodes, that's all being pulled in with a web request.

  • And you need XHR to do that.

  • And it's a slightly different call

  • that I had to make in the Chrome environment

  • versus with just the Angular web environment.

  • And so here's what you can use to get, say, an image.

  • And this is an example of getting an image out of imgur.

  • So I am going to set it up with just a regular HTTP.

  • This is Angular's HTTP thing that

  • can be injected into your app.

  • As I said, the method's Get-- here's the URL.

  • And it's going to be a blob response so that I can actually

  • set it as the source of an image.

  • In a success case, you get all this stuff back.

  • Again, I'm going to ignore everything

  • except for the data that's coming back.

  • I keep scrolling.

  • And there.

  • So I'm going to ignore everything except the data.

  • I have in my HTML this image tag with an ID

  • that I specified, onepod_img.

  • So I'm going to grab a reference to it.

  • And then I can set the source with

  • this window.webkitURL.createObjectURL

  • and then the data.

  • And that's going to allow me to just take that data that's

  • the result of this web request, which is-- actually,

  • it works the same way as the XHR stuff.

  • It's just a slightly different API.

  • And then set that into the image.

  • And there's an error case where it failed.

  • And hopefully, it won't fail for you.

  • And you also have to make sure that imgur

  • is in your permissions.

  • So this is not something that-- it's not my server,

  • so I have to put it in the permissions thing.

  • Once I do that, it should be OK.

  • DARREL SUMI: Right.

  • And you'll notice that there's other things that

  • might need to go into your permissions.

  • So any of the Chrome Apps require

  • you to specify either, like what he said,

  • which domains you might be getting external content

  • from that you put in your approved list.

  • But also things like your storage here.

  • You have to tell it that you're going to use storage.

  • And even for your sockets, if you're

  • going to use the Chrome Socket API,

  • you need to specify that, hey, I'm

  • going to allow TCP connections.

  • JOHN FEIG: And then we're going to keep iterating on this.

  • So one thing that was interesting was-- here's

  • my controller to get individual episodes of a podcast.

  • And this is the method here, makeRequest,

  • that's going to actually get the data for each episode--

  • or for each podcast, actually, and all the episodes within it.

  • So I do the test up top to see.

  • If it's chrome.storage, then I'm in the Chrome environment.

  • There's probably a better way to do that.

  • But that's the thing that I used.

  • And now we get this request URL, which is a really long thing.

  • What's interesting here is I'm actually

  • using a Google service.

  • So with the Content Security Policy, again,

  • and since this is a podcast app, so I

  • needed to go through a single place that's

  • going to return the proper headers.

  • So very basically, with an RSS feed app like mine,

  • you want to be able to subscribe to whoever.

  • And whoever may not have a server that's

  • going to return you the headers that allow you to make

  • requests, like cross-site requests to it.

  • So what Google's done is to set up

  • a service that allows you to get these RSS feeds

  • from a single location.

  • So I can say, ajax.googleapis.com.

  • I'm giving that one permission.

  • It returns me back the proper header

  • so that the requests are valid.

  • And everybody's happy.

  • The one big difference between the HTML5 version

  • and the Chrome version is in the HTML5 version,

  • I can actually use this .jsonp thing, which, basically,

  • this returns as a callback.

  • I don't really need this.

  • There's probably a way that I could get rid of this

  • completely in and do them both the same way.

  • But it's a little nicer.

  • This is how I was doing things before,

  • where it was-- you see a callback equals JSON_CALLBACK

  • and the URL here.

  • And that works with this API.

  • And then for Chrome, if you do that, it'll throw red at you

  • and say, you're not allowed to specify

  • callbacks and responses.

  • So instead, you just do a regular HTTP get.

  • And then in the success, you can still

  • parse the data the same way.

  • So that was the only difference here, really.

  • And then, obviously, the other thing

  • is you've got to be able to load images.

  • And as I showed you before, it's the same deal.

  • So yeah.

  • And then you just iterate until you don't have any more red.

  • There probably are going to be other issues that you'll

  • run into outside of this stuff.

  • DARREL SUMI: So just to recap.

  • You have to really understand where the Content Security

  • Policy's coming from.

  • That's going to be the major source for most of the errors.

  • But you have to look into each of the third-party libraries

  • that you use.

  • A lot of them will make assumptions of what environment

  • they're working in, whether that be how they

  • access sockets, how they access local storage, what they're

  • used to being brought in.

  • So if they use a AMD or Require style of being packaged up,

  • you got to understand how that's all

  • wrapped around those libraries.

  • JOHN FEIG: Yeah.

  • So I can show-- so here's the app in Chrome.

  • Oh, I closed it out.

  • Never mind.

  • Yeah.

  • So here's the app in Chrome.

  • This stuff works.

  • It remembers where you left off because I

  • saved that information in local storage.

  • So I have this method, refreshInterval.

  • So every-- I think it's three seconds, until it's

  • done playing, I send a message to say, update the progress

  • with the current time.

  • DARREL SUMI: And this gets saved into the local storage.

  • JOHN FEIG: It gets saved into the local storage,

  • whereas this data is saved in IndexedDB.

  • I originally wrote it with local storage.

  • And I switched it over to IndexedDB and cut down like-- I

  • just deleted a third of my code.

  • It was really nice.

  • DARREL SUMI: And just to reiterate,

  • the whole point of that is to make sure

  • that the transition from offline mode to online is smooth.

  • So if you do lose connection, it does

  • remember where you left off.

  • And that's really important for the whole user experience

  • point of view.

  • JOHN FEIG: Yeah.

  • And there's definitely some other stuff

  • that I'd like to get to in this app,

  • like basically downloading the audio ahead of time.

  • So that if you have no internet connection,

  • you can still play your stuff.

  • That I did not get to.

  • But it's still a pretty functional tool.

  • It's nicer than some of the other web tools

  • that I've used, which is why I built it.

  • And then we've got-- just throw up some links.

  • We'll make the presentation available to everybody

  • so that they can look at the stuff on their own.

  • We have our app that we're modeling this talk on.

  • This was actually-- I think, basically,

  • all we really needed to do to get KeeChat up and running.

  • The content security stuff was more of a minor issue for us.

  • And then we didn't really have anything outside

  • of this that turned out to be much a problem.

  • It was a pretty straightforward conversion.

  • It took me very little time to get up and running-- really,

  • just a couple days.

  • And this is a pretty decent-size project.

  • So I was very surprised at how quickly

  • we were able to do the conversion.

  • DARREL SUMI: Yeah, definitely.

  • If you come from the Angular setup, it's really easy.

  • JOHN FEIG: And now, we've got Darrel's socket demo linked,

  • which has both the server side and the client side

  • in the same project.

  • DARREL SUMI: Yes.

  • Yep, yep.

  • JOHN FEIG: And then I have two versions

  • of the source for my app.

  • So I created a couple branches so

  • that you can look at before I started the Chrome App

  • conversion and after it was finished.

  • And these specific branches will not have any more commits,

  • so they'll be exactly what I presented to you today.

  • And then I wrote a couple little libraries

  • that work nice with Angular and some of these APIs,

  • like the IndexedDB and localStorage wrapper.

  • And I have to get documentation up for those, but they work.

  • BRAD GREEN: Good, good.

  • Guys, thanks so much.

  • This was a really nice introduction for Chrome Apps

  • and the differences between an HTML5 app and a Chrome App.

  • I think it'll get people started really quickly.

  • And for everyone else, thanks for joining us today.

  • We will see you on the web.

  • We'll share all of these links to the presentation.

  • We love to take questions on Google+ and Twitter.

  • And we'll see you at the next GDL.

  • JOHN FEIG: Thanks for having us.

  • DARREL SUMI: Yeah, thanks for having us.

  • BRAD GREEN: Thanks, guys.

BRAD GREEN: Good morning, everyone.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

A2 初級 美國腔

使用AngularJS構建Chrome應用 (Building Chrome Apps with AngularJS)

  • 126 8
    Joe Huang 發佈於 2021 年 01 月 14 日
影片單字