Unwrap an object in javascript - javascript

I have triggered an event called "search" along with an object.
$(".btn-class").on("click", function() {
var params = {
"searchText": "sometext",
"searchProps": ["prop1", "prop2", "prop3"]
};
COMP.Util.announce("search", {
params: params
})
});
And in another class, I'm listening to this event and processing that object:
var ListView = Backbone.View.extend({
init: function() {
COMP.Util.listenTo("search", $.proxy(this.applySearch, this));
},
applySearch: function(params) {
// do something
}
}
})
My question is when 'params' is passed to applySearch function, it becomes a wrapped object:
params: Object
searchProps: Array[3]
searchText: "sometext"
__proto__: Object
__proto__: Object
If printed, I got:
params=%5Bobject+Object%5D
I want to be able to unwrap 'params' from Object as plain text, and also wonder why wrapping happens in this case
searchProps: Array[3]
searchText: "sometext"
__proto__: Object
Printed like this:
params=searchText=sometext&searchProps=prop1&searchProps=prop2&searchProps=prop3

You can "unwrap" the object with: JSON.stringify(params)
However, I think you might be confusing yourself by adding a key to your params object called params. You might want to leave that extra key out:
$(".btn-class").on("click", function() {
var params = {
"searchText": "sometext",
"searchProps": ["prop1", "prop2", "prop3"]
};
COMP.Util.announce("search",params)
});

You could reuse jQuery method param() which does the job for you.
It accepts as argument arrays and plain objects:
var params = {
"searchText": "sometext",
"searchProps": ["prop1", "prop2", "prop3"]
};
var paramsEncoded = $.param( params );
alert(paramsEncoded)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

Cannot get the result of the Backbone model fetch

My model urlRoot:
urlRoot: function() {
if (this.id != null ) {
return 'notes/' + this.id;
} else return 'notes';
}
Function:
window.show_note = function (note_id) {
var note = new Memo.Models.Note([], { id: note_id });
note.fetch({
success: function (collection, note, response) {
var noteObj = collection.get("0");
var noteView = new Memo.Views.FullNote( {model: noteObj }, {flag: 0 } );
$('.content').html(noteView.render().el);
}
});}
{ id: note_id } - I post this to server to get note by id
I want to do 'set' or 'get' functions on model 'note' after note.fetch in a callback function - success, but only I have is error: 'Uncaught TypeError: note.set is not a function'.
If I do this way: 'var noteObj = collection.get("0");'
I get that I need but I still can`t use get or set.
You should set urlRoot to:
urlRoot: '/notes'
And backbone will figure out that it needs to add the id to the url. (docs)
Assuming Memo.Models.Note is a model and not a collection, the above snippet should be like this:
window.show_note = function(note_id) {
var note = new Memo.Models.Note({ id: note_id });
note.fetch({
success: function (model, response, options) {
var noteView = new Memo.Views.FullNote({
model: model
}, {flag: 0 });
$('.content').html(noteView.render().el);
}
});
};
Note the argument passed to new Memo.Models.Note. A backbone model constructor takes two arguments: attributes and options (docs) as opposed to a collection, which takes models and options (docs). So you'll want to add the hash with the id property as the first argument.
Also note the function signature of the success callback. For a model the success callback takes three arguments: model, response and options (docs). You'll be interested in the model argument because that is the fetched backbone model. response is the raw response data.
I hope my assumptions are right and this is the answer you are looking for.

Jasmine test for object properties

What I'd like to do
describe('my object', function() {
it('has these properties', function() {
expect(Object.keys(myObject)).toEqual([
'property1',
'property2',
...
]);
});
});
but of course Object.keys returns an array, which by definition is ordered...I'd prefer to have this test pass regardless of property ordering (which makes sense to me since there is no spec for object key ordering anyway...(at least up to ES5)).
How can I verify my object has all the properties it is supposed to have, while also making sure it isn't missing any properties, without having to worry about listing those properties in the right order?
It's built in now!
describe("jasmine.objectContaining", function() {
var foo;
beforeEach(function() {
foo = {
a: 1,
b: 2,
bar: "baz"
};
});
it("matches objects with the expect key/value pairs", function() {
expect(foo).toEqual(jasmine.objectContaining({
bar: "baz"
}));
expect(foo).not.toEqual(jasmine.objectContaining({
c: 37
}));
});
});
Alternatively, you could use external checks like _.has (which wraps myObject.hasOwnProperty(prop)):
var _ = require('underscore');
describe('my object', function() {
it('has these properties', function() {
var props = [
'property1',
'property2',
...
];
props.forEach(function(prop){
expect(_.has(myObject, prop)).toBeTruthy();
})
});
});
The simplest solution? Sort.
var actual = Object.keys(myObject).sort();
var expected = [
'property1',
'property2',
...
].sort();
expect(actual).toEqual(expected);
it('should contain object keys', () => {
expect(Object.keys(myObject)).toContain('property1');
expect(Object.keys(myObject)).toContain('property2');
expect(Object.keys(myObject)).toContain('...');
});
I ended up here because I was looking for a way to check that an object had a particular subset of properties.
I started with _.has or Object.hasOwnProperties but the output of Expected false to be truthy when it failed wasn't very useful.
Using underscore's intersection gave me a better expected/actual output
var actualProps = Object.keys(myObj); // ["foo", "baz"]
var expectedProps =["foo","bar"];
expect(_.intersection(actualProps, expectedProps)).toEqual(expectedProps);
In which case a failure might look more like
Expected [ 'foo' ] to equal [ 'foo', 'bar' ]
Here are some new possible solutions too:
There's a module for that: https://www.npmjs.com/package/jasmine-object-matchers
With ES2016, you can use a map and convert the object to a map too
I prefer use this; becouse, you have more possibilities to be execute indivual test.
import AuthRoutes from '#/router/auth/Auth.ts';
describe('AuthRoutes', () => {
it('Verify that AuthRoutes be an object', () => {
expect(AuthRoutes instanceof Object).toBe(true);
});
it("Verify that authroutes in key 'comecios' contains expected key", () => {
expect(Object.keys(AuthRoutes.comercios)).toContain("path");
expect(Object.keys(AuthRoutes.comercios)).toContain("component");
expect(Object.keys(AuthRoutes.comercios)).toContain("children");
expect(AuthRoutes.comercios.children instanceof Array).toBe(true);
// Convert the children Array to Object for verify if this contains the spected key
let childrenCommerce = Object.assign({}, AuthRoutes.comercios.children);
expect(Object.keys(childrenCommerce[0])).toContain("path");
expect(Object.keys(childrenCommerce[0])).toContain("name");
expect(Object.keys(childrenCommerce[0])).toContain("component");
expect(Object.keys(childrenCommerce[0])).toContain("meta");
expect(childrenCommerce[0].meta instanceof Object).toBe(true);
expect(Object.keys(childrenCommerce[0].meta)).toContain("Auth");
expect(Object.keys(childrenCommerce[0].meta)).toContain("title");
})
});
I am late to this topic but there is a a method that allows you to check if an object has a property or key/value pair:
expect(myObject).toHaveProperty(key);
expect({"a": 1, "b":2}).toHaveProperty("a");
or
expect(myObject).toHaveProperty(key,value);
expect({"a": 1, "b":2}).toHaveProperty("a", "1");

How to observe a change on any property in an array or object?

I want to do something like this
Polymer('bc-datalist', {
listitems: [
{name: 'item1'},
{name: 'item2'}
],
observe: {
'listitems': 'change'
},
change: function () {
// do something
}
});
This doesn't work, so my work around is to do something like this:
Polymer('bc-datalist', {
listitems: {
1:{name: 'item1'},
2:{name: 'item2'}
},
observe: {
'listitems.1.name': 'change',
'listitems.2.name': 'change'
},
change: function () {
// do something
}
});
Is there a way of registering a callback when a object/array item has changed?
Correct me if I'm wrong, but it would appear that there's a typo in your first example. Where you mean to reference listitems as the object to observe, you reference listitem instead. Changing this to listitems would make the case for normal top-level properties work:
<polymer-element name="test-el">
<template>
<button on-click="{{clickHandler}}">Click me</button>
</template>
<script>
Polymer('test-el', {
listitems: [
{name: 'item1'},
{name: 'item2'}
],
observe: {
'listitems': 'change'
},
change: function () {
// do something
alert('listitems changed!');
},
clickHandler: function(){
this.listitems.push({ name: 'item3'});
}
});
</script>
</polymer-element>
Onto your question: Polymer does not call the propertyNameChanged callback for properties included in an observe block to the best of my knowledge. This means you will need to specify the exact nested object path (a.b.c) for what you are trying to observe or manually setup the relevant type of Path/Compound observer manually: https://github.com/Polymer/observe-js
Polymer's observe-js library has support for path observation into an object as well as array observation. The former, can be setup similar to what you have (see 2nd example in the docs).
Polymer('x-element', {
observe: {
'a.b.c': 'validateSubPath'
},
ready: function() {
this.a = {
b: {
c: 'exists'
}
};
},
validateSubPath: function(oldValue, newValue) {
var value = Path.get('a.b.c').getValueFrom(this);
// oldValue == undefined
// newValue == value == this.a.b.c === 'exists'
}
});
I'm checking on ArrayObserver support. Will update this post when I know more.
If you have an array of objects and would like to observe changes made to any of those objects' properties, you can try a custom element, <observe-array-items>. It simplifies the process of creating individual PathObservers for all of the objects in the array, and maintains them as items are added/removed from the list.
In your example, you could do something like
<polymer-element name="test-el">
<template>
<observe-array-items array="{{listitems}}"
path="name"
on-array-item-changed="{{handleNameChanged}}">
</observe-array-items>
</template>
<script>
Polymer({
listitems: [
{name: 'item1'},
{name: 'item2'}
],
handleNameChanged: function (e) {
// This will be called whenever the 'name' property
// of an existing object in listitems changes.
// e.detail.oldValue and e.detail.newValue will
// contain the old and current values
// e.detail.index will correspond to the index of the object in the array.
},
listitemsChanged: function() {
// This will be called whenever an object is added or removed from listitems
}
});
</script>
</polymer-element>

Knockout mapping model that contains more models

I have the next data coming from the server and I want to map this data to my model by calling ko.mapping.fromJS(data, AcquisitionDetailsModel);
By inspecting the AcquisitionDetailsModel, after calling the specified method, I see that the model contains acquisitions: Object[0]. Why? I was expecting to find the array of acquisitions inside my model. What I'm doing wrong?
data: Object
acquisitions: Array[2]
0: Object
acquisition: Object
date: "2012-06-20"
__proto__: Object
provider: Object
id: "1"
name: "Some name"
__proto__: Object
truck: Object
mark: "DAF"
model: "95FX"
__proto__: Object
__proto__: Object
1: Object
length: 2
The models are looking like this:
function TruckModel() {
this.mark = ko.observable("");
this.model = ko.observable("");
}
function AcquisitionModel() {
this.date = ko.observable("acquisition_date");
}
function ProviderModel() {
this.id = null;
this.name = ko.observable("name");
}
var AcquisitionDetailsModel = {
acquisitions: ko.observableArray([{
acquisition: new AcquisitionModel(),
provider: new ProviderModel(),
truck: new TruckModel()
}])
}
Actually the mapping was done right but it was me who was doing debugging in the wrong way :)
After ko.mapping.fromJS(data, AcquisitionDetailsModel); if I call AcquisitionDetailsModel.acquisitions() I can see my data.
Also, by calling AcquisitionDetailsModel.acquisitions()[0].truck.model() I still can see the right data.
Stupid me :)

Backbone.js model with collection

I have 2 models and one collection. JobSummary is a model, JobSummaryList is a collection of JobSummary items, and then I have a JobSummarySnapshot model that contains a JobSummaryList:
JobSummary = Backbone.Model.extend({});
JobSummaryList = Backbone.Collection.extend({
model: JobSummary
});
JobSummarySnapshot = Backbone.Model.extend({
url: '/JobSummaryList',
defaults: {
pageNumber: 1,
summaryList: new JobSummaryList()
}
});
When I call fetch on the JobSummarySnapshot object, it gets everything... Except when I move through the summaryList collection they are all of type object and not JobSummary.
I suppose this makes sense since other than the defaults object, it doesn't know that the summaryList should be of type JobSummaryList. I can go through each item and convert it to a JobSummary object, but I was hoping there was a way to do it without having to do it manually.
Here's my test code (working jsfiddle here):
var returnData = {
pageNumber: 3,
summaryList: [
{
id: 5,
name: 'name1'},
{
id: 6,
name: 'name2'}
]
};
var fakeserver = sinon.fakeServer.create();
fakeserver.respondWith('GET', '/JobSummaryList', [200,
{
'Content-Type': 'application/json'},
JSON.stringify(returnData)]);
var callback = sinon.spy();
var summarySnapshot = new JobSummarySnapshot();
summarySnapshot.bind('change', callback);
summarySnapshot.fetch();
fakeserver.respond();
var theReturnedList = callback.getCall(0).args[0].attributes.summaryList;
_.each(theReturnedList, function(item) {
console.log('Original Item: ');
console.log(item instanceof JobSummary); // IS FALSE
var convertedItem = new JobSummary(item);
console.log('converted item: ');
console.log(convertedItem instanceof JobSummary); // IS TRUE
});
UPDATE:
It occurred to me that I could override the parse function and set it that way... I have this now:
JobSummarySnapshot = Backbone.Model.extend({
url: '/JobSummaryList',
defaults: {
pageNumber: 1,
summaryList: new JobSummaryList()
},
parse: function(response) {
this.set({pageNumber: response.pageNumber});
var summaryList = new JobSummaryList();
summaryList.add(response.summaryList);
this.set({summaryList: summaryList});
}
});
This works so far. Leaving the question open in case someone has comment on it....
Your parse() function shouldn't set() anything, its a better practice to just return the attributes, Backbone will take care of setting it. e.g.
parse: function(response) {
response.summaryList = new JobSummaryList(response.summaryList);
return response;
}
Whatever you return from parse() is passed to set().
Not returning anything (which is like returning undefined) is the same as calling set(undefined), which could cause it not to pass validation, or some other unexpected results if your custom validate()/set() methods expects to get an object. If your validation or set() method fails because of that, the options.success callback passed to Backbone.Model#fetch() won't be called.
Also, to make this more generic, so that set()ing to a plain object from other places (and not only from the server response) also effects it, you might want to override set() instead:
set: function(attributes, options) {
if (attributes.summaryList !== undefined && !(attributes.summaryList instanceof JobSummaryList)) {
attributes.summaryList = new JobSummaryList(attributes.summaryList);
}
return Backbone.Model.prototype.set.call(this, attributes, options);
}
You might also find Backbone-relational interesting - it makes it much easier to deal with collections/models nested inside models.
edit I forgot to return from the set() method, the code is now updated

Categories

Resources