I am developing applications with backbone.js and I sometime use JSON based models containing sub-objects, like this one :
{
"key1": "value1",
"key2": "value2",
"key3": {
"key1": "value1"
},
}
Now it seems that the model's method "has" (returning a boolean wether or not a key is part of the model) is not working for sub-objects.
For example, for the data above, this will return undefined
myModel.has("key3.key1")
Any idea how to treat this case?
Thanks
I would suggest, instead of overriding default behaviours, one can easily build this functionality with something similar to
var buildDeepMap = function(baseKey, map, obj){
_.each(obj, function(value, objKey){
var key = baseKey === ''? objKey : baseKey+'.'+objKey;
if(typeof value === 'object'){
buildDeepMap(key, map, value)
}else{
map[key]=value;
}
})
return map;
}
var MyModel = Backbone.Model.extend({
constructor: function(){
Backbone.Model.apply(this, arguments);
this.buildDeepMap();
},
buildDeepMap: function(){
this.deepFlatIndex = buildDeepMap('', {}, this.toJSON());
},
deepHas: function(key){
return _.has(this.deepFlatIndex, key)
}
});
usage can be like this
var data = {
"key1": "value1",
"key2": "value2",
"key3": {
"key1": "value1"
}
}
var myDeepHasModel = new MyModel(data);
console.log(myDeepHasModel.deepHas('key1')); //true
console.log(myDeepHasModel.deepHas('key3.key1')); //true
console.log(myDeepHasModel.deepHas('key3.key2')); //false
Related
I have the following response from API
"features": [
{
"name": "Safety",
"_id": "636a638959d10a2603b8d645",
"values": [
Array of String
]
},
{
"name": "Entertainment",
"_id": "636a64312bbe0cd292a1ffc6",
"values": [
Array of String
]
Which I decode it with :
struct Feature : Codable , Hashable{
var name : String = ""
var values : [Value] = []
}
struct Value : Codable, Hashable{
var value : String = ""
var unit : String = ""
}
And in the view is render it like :
var body: some View {
VStack{
HStack{
Text("Choose Your Features").font(Font.body.bold())
Spacer()
}.padding(.leading, 15)
ScrollView(.vertical, showsIndicators: false){
VStack{
ForEach(Array(features.enumerated()), id: \.offset) { featureIndex, feature in
HStack{
Text(feature.name).font(Font.body.bold())
Spacer()
}.padding(.bottom , 10)
ScrollView(.horizontal, showsIndicators: false){
HStack(spacing : 10){
ForEach(Array(feature.values.enumerated()), id: \.offset) { valueIndex, value in
FeatureCell(isSelected: $isSelected, value: value).onTapGesture{
// here
}
}
}
Divider().padding(10)
}
}.padding(15)
}
}
}
}
The user may select multiple item from each feature values list, Now Im really confused about how to store these selections in an array of features object again, I tried almost every thing like Array, Set and Dictionaries but could not reach any solution.
Update : This is the json object I should send back
{
"features": [
{
"Safety": [
"value1",
"value9",
"value3"
]
},
{
"Entertainment": [
"value7",
"value2",
"value8"
]
}
]
}
Any help or ideas will be much appreciated
You usually want to use a Set to store which items are selected. This set should be a State variable instantiated in the parent view. The onTapGesture closure will add or remove the value to the set. If the FeatureCell needs to know whether the value is selected, simply use the .contains method.
struct FeatureValueSelectionView: View {
// The feature whose values we are displaying
let feature: Feature
// Note: You may have to manually conform Value to the Hashable protocol
#State private var selectedValues = Set<Value>()
var body: some View {
ForEach(feature.values) { value in
FeatureCell(selected: selectedValues.contains(value), value: value)
.onTapGesture { selectedValues.toggle(value) }
}
}
}
For toggling a value in a set, I like to use this simple extension:
extension Set {
public mutating func toggle(_ element: Element) {
if self.contains(element) {
self.subtract([element])
} else {
self.insert(element)
}
}
}
I'm trying to transform my object to list dynamically, so I'm building at view instead of declaring at controller.
I don't want to declare like this: custom_fields.title_field.type_text_field = [] because the title_field is built dynamic, it could be any kind of text like full_name
My json as is:
"custom_fields":{
"title_dynamic_generate_field":{
"type_text_field":{
"name":"John",
"first_name":"Wick"
},
"type_boolean_field":{
"is_badass": true,
"is_good_movie": true
},
"type_select_field": {
"this_select": 1,
"i_got_this": "nope i didnt got this"
}
},
And to be:
"custom_fields":{
"title_dynamic_generate_field":{
"type_text_field":[{
"name":"John",
"first_name":"Wick"
}],
"type_boolean_field":[{
"is_badass": true,
"is_good_movie": true
}],
"type_select_field": [{
"this_select": 1,
"i_got_this": "nope i didnt got this"
}]
},
the object I'm trying to transform into array is type_text_field which can be dynamic too, like type_date_field or type_select_field and so on.
My ng-model is like this:
ng-model="objectApp.application.applicant.custom_fields[layout.dynamic_title][input.type][input.variable]"
the [input.type] is that I'm trying to transform into array, how can I achieve this? I tried to use $index, but got strange results.
We can do it by 2 solutions:
There is a question about your task:
? how you want handle if we have more than one type_text_field in title_dynamic_generate_field? because you want to convert it to "type_text_field":[{},...]
however my answers about the question are:
If we know what's the dynamic params which we want to send theme as json, i mean if we know what is the key of title_dynamic_generate_field or type_text_field, we do as this sample:
var data = {
"custom_fields": {
dynamicParamIs1: 'title_dynamic_generate_field',
dynamicParamIs2: 'type_text_field',
"title_dynamic_generate_field": {
"type_text_field": {
"name": "John",
"first_name": "Wick"
}
}
}
}
var paramHelper1 = json.custom_fields[json.custom_fields.dynamicParamIs1];
var paramHelper2 = json.custom_fields.dynamicParamIs2;
var solutionA = function (object, as) {
var array = [];
for (var key in object) {
var newObject = object[key];
array.push(newObject);
}
object[as] = array;
}
solutionA(paramHelper1, paramHelper2);
We changed a model of our json which can help us to detect (find) the keys
If we don't know what is the dynamic params are, we do as this:
var data = {
"custom_fields": {
"title_dynamic_generate_field": {
"type_text_field": {
"name": "John",
"first_name": "Wick"
}
}
}
}
var solutionB = function (json) {
var array = [];
for (var key in json) {
var j1 = json[key];
for (var key2 in j1) {
var j2 = j1[key2];
for (var key3 in j2) {
var fullObject = j2[key3];
array.push(fullObject);
j2[key3] = array;
}
}
}
}
solutionB(data);
This sample is manual which we use nested for to detect the keys name
Is there any way to search inside nested elements in smart-table? I feed the table with data from a REST Api that consists of the following form:
{
"id": 1,
"small_name": "Foo",
"large_name": "Bar Foo",
"variants": [{"value": "0"}, {"value": "1"}]
}
What I want to achieve is the possibility to filter the data through the value property of the objects inside the variants.
From the Smart Table documentation:
"The stSetFilter replaces the filter used when searching through Smart Table. When the default behavior for stSearch does not meet your demands, like in a select where one entry is a substring of another, use a custom filter to achieve your goals."
http://lorenzofox3.github.io/smart-table-website/
There is also an example available at that site.
I'll post the solution for my problem, maybe it can help someone.
angular.module('YourModule').filter('CustomFilter', [
'$parse',
function ($parse) {
return function(items, filters) {
console.log(items, filters);
var itemsLeft = items.slice();
Object.keys(filters).forEach(function (model) {
var value = filters[model],
getter = $parse(model);
itemsLeft = itemsLeft.filter(function (item) {
if (model === 'value') {
var variants = item.variants.filter(function (variant) {
return getter(variant).match(value);
});
return variants.length;
} else {
return getter(item).match(value);
}
});
});
return itemsLeft;
}
}
])
I'm sure I make one of these Backbone newbie mistakes but after a hour of searching around I didn't found a solution.
Here's the problem: When I try to get a filtered model from my collection theres a type error "productCollection.getProductByName("M020012").toJSON is not a function".
But if I change the filter method to a simple "return this.at(0)" I get a valid model.
Why is that and what is the solution?
Here's the JSFiddle
var products = [{
"name": "M020013",
"gender": "M",
"pictures": [{
"picture": {}}]},
{
"name": "M020012",
"gender": "M",
"pictures": [{
"picture": {}}]},
{
"name": "M020011",
"gender": "M",
"pictures": [{
"picture": {}}]}
];
var Product = Backbone.Model.extend({});
var ProductCollection = Backbone.Collection.extend({
model: Product,
getProductByName: function(productName) {
//return this.at(0);
return this.filter(
function(product) {
return product.get('name') === productName;
});
}
});
var productCollection = new ProductCollection();
productCollection.on('reset', function() {
console.log('reset');
console.log(productCollection.getProductByName('M020012'));
console.log(productCollection.getProductByName('M020012').toJSON());
});
productCollection.reset(products);
It's because filter returns an array of models. And an Array in javascript does not have a toJSON function.
Since you want to return a model instead of an array, then you can use the find in place of filter. The find method returns the first model that matches the criteria
Here's what the code would look like:
getProductByName: function(productName) {
return this.find(function(production) {
return production.get('name') === productName;
});
}
I'm trying to understanding backbone code. I read this
http://addyosmani.com/blog/building-spas-jquerys-best-friends/
then I plan to change this
if (this._index === null){
$.ajax({
url: 'data/blog.json',
dataType: 'json',
data: {},
success: function(data) {
ws._data = data;
console.log(ws._data);
ws._blogs = new BlogCollection(data.rows);
console.log(ws._blogs);
ws._index = new IndexView({model: ws._blogs});
Backbone.history.loadUrl();
}
});
return this;
}
return this;
use collection fetch
if (this._index === null){
ws._data = new BlogCollection;
ws._data.fetch({success: function(data){console.log(data.models[0].attributes);}});
//ws._blogs = new BlogCollection(ws._data.rows);
//ws._index = new IndexView({model: ws._blogs});
Backbone.history.loadUrl();
}
with this collection
var BlogCollection = Backbone.Collection.extend({
model: Blog,
url : 'data/blog.json',
parse: function(response){
return response;
}
when I read the response from collection, it's give same value as using jquery ajax.
but when I use fetch in controller, why I have to access data.models[0].attributes); to get the same data return.
this is my json
{ "page":"1", "total": "5", "records": "25", "rows":
[
{
"title": "Classic Pop",
"author": "Michael Jackson Collection",
"image": "images/1.jpg",
"tags": ["xyz", "abc", "def"],
"slug" : "classic-pop",
"url": "http://www.localhost/news",
"intro": "hello test",
"content": "hello test, alfjldsajfldjsflja dljfldasjfa jfljdasfl jfldsjf jljdafl jl"
},
{
"title": "Modern Pop/R&B",
"author": "Bruno Mars and Others",
"image": "images/54.jpg",
"tags": ["test", "test2", "test3"],
"slug" : "modern-pop-rb",
"url": "http://www.localhost/news",
"intro": "hello test 2",
"content": "hello test, alfjldsajfldjsflja dljfldasjfa jfljdasfl jfldsjf jljdafl jl"
}
] }
how to make fetch works right??
When extending your Backbone Collection, you should define a parse function that returns an array of rows that represent the models contained in your collection.
In your case, it must be an array of data, with each object in the array representing your Blog model, so you need to return the rows property:
var BlogCollection = Backbone.Collection.extend({
model: Blog,
url : 'data/blog.json',
parse: function(response){
return response.rows;
}
});
Then if your model has a parse function, it will get the data for each object contained in the array, in case you need to do anything with the data before the model's attributes are set:
var Blog = Backbone.Model.extend({
//data will contain one of the items returned from the collection's 'parse' function.
parse: function(data){
return data;
}
});
This will ensure that the Backbone collection will properly create and populate the models represented in the collection with the data.
You will probably want to expose the other metadata (page, total, records) on the collection too, perhaps with a property that is also a Backbone.Model of page/total/records.