MSPage addLayer undefined - sketchapp

I'm writing a demo that working with Layer and Page.
My code looks like:
layer = [MSLayer new]
[[doc currentPage] addLayer:layer] //<= addLayer is undefined
I saw in the Sketch plugin API for MSPage has the addLayer function.
I'm running Sketch 3.2.2

I inspected the MSLayerGroup class and found that addLayer is replaced by addLayers, which takes an array of MSLayer.
Now I use the following code to add layer into group:
// In Sketch 3.3.2 (or earlier), addLayer is replaced by addLayers.
var addLayerToGroup = function(group, layer) {
if (group.addLayer == undefined) {
[group addLayers:[NSArray arrayWithObjects:layer]];
} else {
[group addLayer:layer];
}
}

Related

Why is Zone.js changing how AngularJS evaluates attributes?

I have an app with both Angular (2+) and AngularJS (1.x). We are using a third party AngularJS library that reads an object from its attrs array in a link function, like so:
//3rd party lib code:
module.directive('test', () => ({
template: `Look at the console`,
link(elt, scope, attrs) {
console.log('link attrs.props', attrs.props);
}
}))
Template:
<!-- someObject = {name: 'foo'} -->
<test props="{{someObject}}"></test>
We just upgraded to the latest version of AngularJS and we noticed a problem. Normally, attrs.props evaluates to a string representation of the object. Instead of getting a stringified object, we're getting "[object Object]"
I attempted a minimal reproduction but I couldn't reproduce the problem, until I tried importing Zone.js as you can see on this stackblitz:
https://stackblitz.com/edit/angularjs-attrs-test?file=app.js
If Zone.js is imported (which we need for Angular 2+), then attrs.props is "[object Object]". Without it, attrs.props is {name: 'foo'}.
Is this a known issue? Is there a workaround?
It is a good practice to always load ZoneJS before anything else, otherwise some strange problems like that can happen. In you example, if you simple move the ZoneJS import to the first line, it solves the problem.
ZoneJS overrides Object.prototype.toString method which leads to unexpected behavior in AngularJS stringify function:
function stringify(value) {
if (value == null) { // null || undefined
return '';
}
switch (typeof value) {
case 'string':
break;
case 'number':
value = '' + value;
break;
default:
if (hasCustomToString(value) && !isArray(value) && !isDate(value)) {
\/
true
value = value.toString(); // will be called since zone js overrided this method
} else {
value = toJson(value); // will be called without zonejs
}
}
return value;
}
In order to work around it you can disable this patch:
window.__Zone_disable_toString = false;
import 'zone.js/dist/zone';
Forked Stackblitz

Angular2 Tutorial: How is the ID variable in this section being auto incremented?

In This Section of the Angular2 Tutorial there is functionality to add new items to the array. When added, the ID is automatically incremented but I can't figure out what process is doing that.
I know that Arrays.push() returns the length of the array, is that length automatically inserted into the id variable in the Hero class?
In hero.services.ts there is this block of code to create a hero:
create(name: string): Promise<Hero> {
return this.http
.post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
.toPromise()
.then(res => res.json().data)
.catch(this.handleError);
}
In the heroes.component.ts there is the add
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.create(name)
.then(hero => {
this.heroes.push(hero);
this.selectedHero = null;
});
}
The tutorial is using the angular 2 in-memory-web-api library. It is handling the post that is being made to the heroes url. The handler can be seen in this file on line 328:
https://github.com/angular/in-memory-web-api/blob/master/in-memory-backend.service.js
Inside that handler the id is generated via a call to the genId function whose implementation is on line 257:
InMemoryBackendService.prototype.genId = function (collection) {
// assumes numeric ids
var maxId = 0;
collection.reduce(function (prev, item) {
maxId = Math.max(maxId, typeof item.id === 'number' ? item.id : maxId);
}, null);
return maxId + 1;
};
It uses the default functionality of InMemoryDbService .
You can find the mock/reference in app/in-memory-dataservice.ts
import { InMemoryDbService } from 'angular2-in-memory-web-api
The angular source for the service can be found here: in-memory-backend.service.t (line 326)
In the create method of the service, a web api gets called and posted the hero's name to it, returning a full hero object with id and name fields.
So I guess the incrementing and "saving" happens behind the curtains at that web api.

Using Angular Material, is it possible to close a specific dialog

I have an AngularJS app using the Angular Material UI framework.
The app has different mechanisms showing dialogs (e.g error and loading spinner) and it would be preferable to only close one specifically chosen in certain scenarios, e.g. when an AJAX request is finished fetching data, I would like my loading spinner to close, but not any error dialog that may be the result of the fetching.
What I can find in documentation and code doesn't agree (though code should win the argument):
Documentation says only the latest can be closed, with an optional response
The code says the latest, a number of latest or all open can be closed, with an optional reason
Example in the documentation says a specific dialog can be closed, with a flag denoting how or why
I have made a demo of my intent, as MCV as possible – these are the highlights:
var dialog = {},
promise = {};
function showDialogs(sourceEvent) {
showDialog(sourceEvent, "one");
showDialog(sourceEvent, "two");
}
function showDialog(sourceEvent, id) {
dialog[id] = $mdDialog.alert({...});
promise[id] = $mdDialog.show(dialog[id]);
promise[id].finally(function() {
dialog[id] = undefined;
});
}
function closeDialogs() {
$mdDialog.hide("Closed all for a reason", {closeAll: true});
}
function closeDialogLatest() {
$mdDialog.hide("Closed from the outside");
}
function closeDialogReason() {
$mdDialog.hide("Closed with a reason");
}
function closeDialogSpecific(id) {
$mdDialog.hide(dialog[id], "finished");
}
EDIT:
I know the code always wins the argument about what happens, but I wasn't entirely sure it was the right code I was looking at.
I have updated the examples to better test and illustrate my point and problem. This shows things to work as the code said.
What I'm really looking for is whether it might still be possible to achieve my goal in some other way that I didn't think of yet.
Using $mdPanel instead of $mdDialog I was able to achieve the desired effect; I forked my demo to reflect the changes – these are the highlights:
var dialog = {};
function showDialogs() {
showDialog("one");
showDialog("two");
}
function showDialog(id) {
var config = {...};
$mdPanel.open(config)
.then(function(panelRef) {
dialog[id] = panelRef;
});
}
function closeDialogs() {
var id;
for(id in dialog) {
closeDialogSpecific(id, "Closed all for a reason");
}
}
function closeDialogSpecific(id, reason) {
var message = reason || "finished: " + id;
if(!dialog.hasOwnProperty(id) || !angular.isObject(dialog[id])) {
return;
}
if(dialog[id] && dialog[id].close) {
dialog[id].close()
.then(function() {
vm.feedback = message;
});
dialog[id] = undefined;
}
}
I would suggest having two or more dialogs up at the same time isn't ideal and probably not recommended by Google Material design.
To quote from the docs
Use dialogs sparingly because they are interruptive.
You say:
when an AJAX request is finished fetching data, I would like my
loading spinner to close, but not any error dialog that may be the
result of the fetching.
My solution here would be to have one dialog which initially shows the spinner. Once the request is finished replace the spinner with any messages.

How to properly save self reference with ES6 classes?

Honestly, I'm not sure of what is the cause for the behavior: systemjs, babel or my own fault. I'm using class for custom control controller and saving class reference in self variable. Apparently that gets overriden by any subsequent controller instances.
I created a simple repository to demonstrate:
clone, install, run live-server or your preferred server. You will see 2 buttons, each is a custom control. Clicking on a button only affects one control.
https://github.com/alexkolt/selfIsThis
How can I get this working with ES6 class?
I should have posted the code, sorry.
The reason you'd want to save reference to self is for example in callbacks calling this might result in a different reference.
I was trying to do this:
var self;
class Test {
constructor(dependency) {
self = this;
self.dependency = dependency;
}
method() {
self.dependency().then(value => self.property = value);
}
}
Like it was mentioned before the self becomes shared when declared outside of the module. I didn't realize that would happen as files would be wrapped in a closure. Joe Clay answer is correct, but to do what I was trying to do self needs to be declared in every method that needs it.
class Test {
constructor(dependency) {
this.dependency = dependency;
}
method() {
var self = this;
this.dependency().then(value => self.property = value);
}
}
You're not really using ES6 classes right. You don't need to save a reference to this - just access it directly in class methods. The way you have it at the minute, all your instances of CustomControlController are sharing a single self variable.
class CustomControlController {
constructor() {
this.value = 0;
}
click() {
var newValue = this.value * 2;
this.value = newValue;
}
}
export default CustomControlController;

How to use annotorious with angular

I'm trying to best integrate annotorious with angular. The Annotorius library although described, it is particularly difficult to debug. (see what I mean at annotorious.debug.js )
So far, I created my own solution with angular-annotorious (free MIT open source project DISCLAIMER: i'm the maintainer of it.), that offers 2 directives for tag and attribute cases, also a service to access the anno object.
I'm facing problems with dynamic contents, like sliders.
(note that I do know that for ng-src images special treatment may be needed due to the img src cycle)
The annotated images with an unique URL, once recreated in the DOM tree, it does not seem to get annotated once again if we call
anno.makeAnnotatable(img)
To reproduce it, see the colorbox example
(colorbox is a free open source modal library)
Are there any interesting ideas how to integrate/annotate dynamic content that may reappear?
I found the solution, instead of doing a "shutting down" annotorious, we simply call the reset function in the onLoad and onCleanup events which is critical for dynamic content like colorbox or fancybox.
The code for dynamic integration with colorbox looks like this:
$scope.annotateColorbox03 = {
rel: 'img-group-01',
slideshow: false,
open: false,
onComplete: function () {
console.log('03-complete');
var photo = colorboxService.getCurrentPhoto();
if (photo.src) {
console.log('annotateColorbox03 ' + photo.src);
annotoriousService.makeAnnotatable(photo);
var annotations = annotoriousService.getAnnotations(photo.src);
console.log(annotations);
if (annotations && annotations.length > 0) {
annotoriousService.showAnnotations(photo.src);
colorboxService.resize();
}
}
},
onLoad: function () {
console.log('03-onLoad');
annotatableImage();
},
onCleanup: function () {
console.log('03-cleanup');
annotatableImage();
}
};
function annotatableImage() {
var photo = colorboxService.getCurrentPhoto();
if (photo && photo.src) {
//required
annotoriousService.reset(photo.src);
}
}
The working example is now at:
Annotorious in Colorbox Gallery
This example relies on angular-annotorious using the annotoriousService.
DISCLAIMER: i'm the maintainer of angular-annotorious which is a free MIT open source project.

Resources