"The NativeScript Book" by Branstein brothers Errata?


#1

I am working thru the NativeScript book (which is really good) and finding some issues starting at about page 156 with the PetScrapbook project. Since I am on the beginning of the learning curve with NativeScript, wondering if others are experiencing issues with this and if this is the place to get into specifics or if something is setup elsewhere? Thanks!


#2

You might want to ask the authors themselves

CC @NathanaelA @wwwalkerrun @bradwaynemartin


#3

Thanks for the reply. I was actually referring to the recent “The NativeScript Book: Building Mobile Apps with skills you already have” by the Branstein brothers. I’ll take your point tho and try to directly reach out to them.


#4

Awesome that you’re enjoying the book so far. The authors handles are @mikeb and @Nick, @Pete.K was thinking of the other recently released {N} book :smile:

Would you provide a little more detail on the problem you’re having? Is there a specific error that you’re getting that would help us debug this? Or is the example on that page just not working?

Thanks!


#5

Thanks @tjvantoll! @catwood2 if you want to post specifics here I’m sure that is fine and we’ll help you through them!


#6

Awesome @Nick!

Here’s what I am seeing so far that I think may be issues. I’ll just note that I am doing copy/paste straight from the code sections as I work along.

  1. Issue - Listing 8.13 results in error:

TypeError: Cannot read property ‘toLocaleDateString’ of undefined. scrapbook-page.js line:15

Resolution: explicitly define date in scrapbook observable. Then output reads:

"[NSDebugAdapter] Connection to target application successfully enabled
JS: You have made undefined
JS: Age: Sat Nov 25 2017
JS: Gender selected:undefined
"
Had to define title and gender as well to get expected output. Page 158 notes that should not
have to explicitly supply the parameters but it was the only way I could get it to work.

  1. Issue - Listing 8.14 refers to function not yet defined, so when run up shows:

JS: Cannot find function or filter: calcAge
This is defined in the js file further down, so, sequence issue.
Also, Figure 8.14 screenshot correct but onTap results in exceptions (guess to be expected given js not yet updated.

  1. Issue - pg 161 (8.1.5 & 8.1.6) after implementing I’m losing my gender picker and tossing exceptions on console logging lines.

I did go out to git to look in the repository, but the js and xml files are in final state for chapter 8…so, wasn’t able to make a comparison there with what’s in the listings. That said, I am completely new to this so I am open to the idea I am doing something stupid!


#7

Thanks for the detailed response this will be helpful for us keeping everything up to date and working in the book!

8.13 - I think this may be due to a recent change but I will need to test this. Either way looks like you got it working and we’ll update the book to just include those fields.

8.14 - This function should show up until the next two listings (it looks like this one got mixed up with an older version sorry!)

I believe this should be

<Page loaded="onLoaded">
	<StackLayout>
		<Label text="{{ title, title + ' Scrapbook Page' }}" />
		<TextField class="header" text="{{ title }}" hint="Enter title..."/>
		<Label text="Age: " />
        <DatePicker date="{{ date }}" /> 
		<Label text="Gender: " />
		<ListPicker items="{{ genders }}" selectedIndex="{{ gender }}" />
		<Button tap="onTap" text="Done"/>
	</StackLayout>
</Page>

Edit: I may have spoken a bit too soon about 8.15/16 - can you list your full code or do you happen to have it in a git repo? Something looks a bit out of order but I’m not quite sure what and I’m not seeing errors in my local copy right now.

Thanks again for all the details and if you find anything else - let us know so we can fix it!


#8

I haven’t set up version control yet. 8.15 is straight copy/paste from the book. Below I’ve pasted the current scrapbook-page.js. The copy of the book exports.onLoaded code in 8.16 is what was having issues and is commented out. The uncommented version is tweaked to use

observable.fromObject

instead of

observable.Observable

in the book. That got me the picker back and the age field populating (but with NaN as an age result)…given where I’m at on the learning curve, not sure I appreciate the observable.fromObject versus observable.Observable implications! In any event, that’s where things sit at the moment.
Thanks!

var observable = require("data/observable");
exports.onLoaded = function(args) {
 var page = args.object;
 var scrapbook = new observable.fromObject({
   
   genders: ["Female", "Male", "Other"], 
   title: "",
   
   gender: "",
   calcAge: function(birthDate) {
     
     var now = Date.now();
     var diff = Math.abs(now - birthDate) / 1000 / 31536000; 
     return diff.toFixed(1);
     
   },
   
   
   
 }); 
 page.bindingContext = scrapbook;
};

#9

Glad you’re back on track - I’ll work on getting the samples updated as we missed a couple of items obviously.

The observable.fromObject was actually a breaking change in NS 3.0 so that’s how that one snuck in there. You should get NaN until you actually move the date picker since technically the field that it is binding to wasn’t initialized yet. Thanks for reading!


#10

Cool. The NaN remains even after moving the date picker so not sure the deal there.

In moving on, listing 8.19 also required changing to the observable.fromObject to get it working. Is that to be expected across the board?

Thanks for the help and writing this book - really well done!


#11

Thanks for the great feedback. Yes I believe that listing 8.19 should use observable.fromObject as well. I’ll just blame these mixups on Mike :slight_smile:


#12

had the same issue as well. Seemed obvious enough to fix for me but no assumptions should be made with how-to books.


#13

One suggestion I would make for all students of this book is to use a diff checker at any point you are stuck and compare with the finished code examples. This is how I figured out the problem for myself as it concerns: observable.fromObject instead of observable.Observable

When you’re new to a mobile platform it’s very hard to know whether you did something in the JS or maybe it’s something down the line with Android. Lots of moving parts!


#14

It’s a small thing I guess but for example in this code here:

var fileSystem = require("file-system"); // #A
var fileSystemService = function () {
this.file = fileSystem.knownFolders.documents().getFile("scrapbook.json");
};
fileSystemService.prototype.getPages = function () { // #B
var pages = [];
if (this.file.readTextSync().length !== 0) {
pages = JSON.parse(this.file.readTextSync()); // #C
}
return pages;
}
fileSystemService.prototype.savePage = function (scrapbookPage) { // #D
var pages = this.getPages();
var index = pages.findIndex(function (element) { // #E
return element.id === scrapbookPage.id;
});
if (index !== -1) {
pages[index] = {
id: scrapbookPage.id,
title: scrapbookPage.title,
gender: scrapbookPage.gender,
year: scrapbookPage.year,
month: scrapbookPage.month,
day: scrapbookPage.day
};
}
else {
pages.push({
id: scrapbookPage.id,
title: scrapbookPage.title,
gender: scrapbookPage.gender,
year: scrapbookPage.year,
month: scrapbookPage.month,
day: scrapbookPage.day
});
}
var json = JSON.stringify(pages); // #F
this.file.writeText(json);
// #F
};
exports.fileSystemService = new fileSystemService(); 

your constructors are always lowercase. Is there a reason for this that I’m not aware of?


#15

I am having an issue running the scrapbook with the code updates made in section 9.1 page 187. I made the necessary observable/fromObject updates but the rest is copy and paste from the book. The error on deploying is:

can not read property of getPages of undefined, undefined

I definitely took @Jaybo point of referring to the repo - but, with where I am on the learning curve with both javascript and NativeScript I couldn’t spot the issue. So, spent some trying to see if I could sort but now waving the flag! Thanks for any pointers.


#16

can you make a temp repo on github so I can clone it and help you?


#17

We will at least need to see your code to be able to help. It could be anything from a typo to a bad require call.


#18

I’ll get the temp repo setup. So, the code was running for you at this same point in the book? Thanks!


#19

when you see a prop of undefined it usually means you haven’t required the file in properly or it wasn’t instantiated correctly. did you type the path correctly? I don’t know I’m just stabbing in the dark.


#20

I’ll paste here since faster than me getting git setup at the moment. Thanks!

scrapbook-page.js

var observable = require(“data/observable”);
var observableArray = require(“data/observable-array”);
var frame = require(“ui/frame”);
var fileSystemService = require("~/data/fileSystemService");

function scrapbookPageModel() {
// #A
var model = new observable.fromObject({
id: id,
genders: [“Female”, “Male”, “Other”],
title: “”,

gender: "",
calcAge: function(birthDate) {
  var now = Date.now();
  var diff = Math.abs(now - birthDate) / 1000 / 31536000;
  return diff.toFixed(1);
}

});

return model;
}

/*exports.onLoaded = function(args) {
var page = args.object;
var scrapbook;
if (page.navigationContext != null) {
// #B
scrapbook = page.navigationContext.model;
} else {
scrapbook = new observable.fromObject({
pages: new observableArray.ObservableArray(new scrapbookPageModel())
});
}
page.bindingContext = scrapbook;
}; */

exports.onLoaded = function(args) {
var page = args.object;
var scrapbook = new observable.fromObject({
pages: new observableArray.ObservableArray()
});
var pages = fileSystemService.fileSystemService.getPages();
if (pages.length !== 0) {
pages.forEach(function(item) {
var model = new scrapbookPageModel(item.id); // #B
model.title = item.title;
model.gender = item.gender;
model.year = item.year;
model.month = item.month;
model.day = item.day;
scrapbook.pages.push(model);
});
}
page.bindingContext = scrapbook;
};

exports.onAddTap = function(args) {
var page = args.object;
var scrapbook = page.bindingContext;
frame.topmost().navigate({
moduleName: “views/scrapbookUpdate-page”,
context: { model: new scrapbookPageModel(scrapbook.pages.length) } // #A
});
};
exports.onItemTap = function(args) {
var page = args.object;
var scrapbook = page.bindingContext;
frame.topmost().navigate({
moduleName: “views/scrapbookUpdate-page”,
context: { model: scrapbook.pages.getItem(args.index) } // #B
});
};
/*exports.onAddTap = function(args) { // #A
var page = args.object;
var scrapbook = page.bindingContext;
scrapbook.pages.push(new scrapbookPageModel()); // #B
frame.topmost().navigate({
moduleName: “views/scrapbookUpdate-page”,
context: { model: scrapbook, index: scrapbook.pages.length - 1 } // #C
});
};

exports.onItemTap = function(args) {
// #C
var page = args.object;
var scrapbook = page.bindingContext;
frame.topmost().navigate({
moduleName: “views/scrapbookUpdate-page”,
context: { model: scrapbook, index: args.index } //# D
});
};*/

fileSystemService.js

var fileSystem = require(“file-system”); // #A
var fileSystemService = function() {
this.file = fileSystem.knownFolders.documents().getFile(“scrapbook.json”);
};
fileSystemService.prototype.getPages = function() {
// #B
var pages = [];
if (this.file.readTextSync().length !== 0) {
pages = JSON.parse(this.file.readTextSync()); // #C
}
return pages;
};
fileSystemService.prototype.savePage = function(scrapbookPage) {
// #D
var pages = this.getPages();
var index = pages.findIndex(function(element) {
// #E
return element.id === scrapbookPage.id;
});
if (index !== -1) {
pages[index] = {
id: scrapbookPage.id,
title: scrapbookPage.title,
gender: scrapbookPage.gender,
year: scrapbookPage.year,
month: scrapbookPage.month,
day: scrapbookPage.day
};
} else {
pages.push({
id: scrapbookPage.id,
title: scrapbookPage.title,
gender: scrapbookPage.gender,
year: scrapbookPage.year,
month: scrapbookPage.month,
day: scrapbookPage.day
});
}
var json = JSON.stringify(pages); // #F
this.file.writeText(json); // #F
};