backbone model set and unset in same single method call - backbone.js

Assume I have one backbone model with state
var myModel = new Backbone.Model(
{
key1:'value1',
key2:'value2',
key3:'value3'
});
myModel.on('all', function allHanlder () {
console.log(arguments);
})
I need remove key1 and key2 and change key3. Possible options are
unset key1 and key2 , set key3 attribute
clear model and set key3
option 1 results in 3 change, 3 change attribute events
myModel.unset('key1');
myModel.unset('key2');
myModel.set({key3:'newValue3'})
//events
["change:key1", Object { cid="c1355", attributes={...}, _changing=true, more...}, undefined, Object { unset=true}]
["change", Object { cid="c1355", attributes={...}, _changing=true, more...}, Object { unset=true}]
["change:key2", Object { cid="c1355", attributes={...}, _changing=true, more...}, undefined, Object { unset=true}]
["change", Object { cid="c1355", attributes={...}, _changing=true, more...}, Object { unset=true}]
["change:key3", Object { cid="c1355", attributes={...}, _changing=true, more...}, "newValue3", Object {}]
["change", Object { cid="c1355", attributes={...}, _changing=true, more...}, Object {}]
option 2 will result in 2 change change:attribute events
myModel.clear()
myModel.set({key3:'newValue3'})
//output
["change:key1", Object { cid="c1356", attributes={...}, _changing=true, more...}, undefined, Object { unset=true}]
["change:key2", Object { cid="c1356", attributes={...}, _changing=true, more...}, undefined, Object { unset=true}]
["change:key3", Object { cid="c1356", attributes={...}, _changing=true, more...}, undefined, Object { unset=true}]
["change", Object { cid="c1356", attributes={...}, _changing=true, more...}, Object { unset=true}]
["change:key3", Object { cid="c1356", attributes={...}, _changing=true, more...}, "newValue3", Object {}]
["change", Object { cid="c1356", attributes={...}, _changing=true, more...}, Object {}]
change:attribute events are fair, but I need to minimise change triggers. Something like below.
myModel.someMagicSet({key3:'newValue3'})
//output
["change:key1", Object { cid="c1355", attributes={...}, _changing=true, more...}, undefined, Object {}]
["change:key2", Object { cid="c1355", attributes={...}, _changing=true, more...}, undefined, Object {}]
["change:key3", Object { cid="c1355", attributes={...}, _changing=true, more...}, "newValue3", Object {}]
["change", Object { cid="c1355", attributes={...}, _changing=true, more...}, Object {}]
Is there is a way I can achieve it without overriding backbone behaviours?

myModel.unset('key1', {silent: true});
myModel.unset('key2', {silent: true});
myModel.set({key3:'newValue3'}, {removed: ['key1', 'key2']});
This will do what you want while only triggering one change event. You are passing in the removed items to your handler in the options object. In your handler, your code should look like this:
myModel.on('all', function allHandler (model, options) {
console.log(options.removed); // this should show you what has been removed before
// other code
})
Alternately, you could pass a flag in your options with each event which you could analyze in your handler, which would determine whether the handler was run or not. This would give your handler access to what has been changed, but it would have to store it outside of it's own scope in order to access it the next time.
myModel.unset('key1', {finished: false});
myModel.unset('key2', {finished: false});
myModel.set({key3:'newValue3'}, {finished: true});
EDIT: Here is a one liner that should do everything:
myModel.clear({reset: {key3: 'newValue3'}});
and then in your handler do this
myModel.on('all', function allHandler (model, options) {
if (options.reset){
model.set(options.reset);
}
// other code
})
The thing you should watch out for is that clear removes the id too, so if you need that you should pass it in with your options:
myModel.clear({reset: {key3: 'newValue3', id: myModel.get('id')}});

Related

Getting a specific object from a group of object in angularjs

In a gridlist,I want to get only specific objects in an array.Here is my code.
$scope.incIncObj = [];
$scope.gridList = {};
$scope.gridList.incIncObj = studyValue.incluExcluCriteria;
console.log(studyValue.incluExcluCriteria);
In c#,the datas are like this,
var incluExcluCriteria = (from IE in _context.Incl_Excl_Criteria
where IE.Study_ID == request.Study_Id
select new
{
IE.Incl_Excl_Id,
IE.Study_ID,
IE.Criteria_Type,
IE.Criteria
}).ToList();
In the console,I am getting all the list such as,
[Object { Incl_Excl_Id=2992, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2993, Study_ID=350, Criteria_Type="E", more...},
Object { Incl_Excl_Id=2994, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2995, Study_ID=350, Criteria_Type="E", more...},
Object { Incl_Excl_Id=2996, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2997, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2998, Study_ID=350, Criteria_Type="E", more...},
Object { Incl_Excl_Id=2999, Study_ID=350, Criteria_Type="E", more...}]
But I want to get only the object which has Criteria_Type="I" like this,
[Object { Incl_Excl_Id=2992, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2994, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2996, Study_ID=350, Criteria_Type="I", more...},
Object { Incl_Excl_Id=2997, Study_ID=350, Criteria_Type="I", more...}]
Give me any suggestion
use javascript filter prototype method to filter the array
$scope.gridList.incIncObj = studyValue.incluExcluCriteria.filter(function(obj){
return obj.Criteria_Type ==="I"
})
console.log(studyValue.incluExcluCriteria);
note that your json is invalid. use colon instead of = when you are assign value to property
this { Incl_Excl_Id=2992, Study_ID=350, Criteria_Type="I"} should be change
to this { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"I"}
var studyValue = [{ Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"T"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"I"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"S"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"I"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"I"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"K"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"I"}, { Incl_Excl_Id:2992, Study_ID:350, Criteria_Type:"I"} ]
var incIncObj = studyValue.filter(function(obj){
return obj.Criteria_Type ==="I"
})
console.log(incIncObj);
Why to write custom filter ? This code may not work in code snippet.But try this pipe filter in your application.I just tried myself and gives you the short & easy answer for your query.
<div ng-repeat="c in collection | filter : 'I'">
<span>{{ c.Incl_Excl_Id}}</span>
<span>{{c.Study_ID}}</span>
<span> {{c.Criteria_Type }}</span>
</div>
</div>
Another angualrjs approach would be using the filter provided by angular.
First inject $filter service in your controller. Then use it like below-
var filteredList = $filter('filter')(youFullList, {Criteria_Type:"I"})
for more details- https://docs.angularjs.org/api/ng/filter/filter

Elasticsearch query parse failure

Why does this ES multi-match query return a 400 error (bad request)?
"query": {
"multi_match": {
"query": searchTerms,
"fields": ["content", "title"],
"operator": "and"
}
},
size: 100,
from: 0,
highlight: {
fields: {
"title": {number_of_fragments: 0},
"content": {number_of_fragments: 10,fragment_size: 300}
}
}
}
I'm using this query in conjunction with AngularJS UI Bootstrap Typeahead code like this
uib-typeahead="query as query._source.ymme for query in getSuggestions($viewValue)" typeahead-on-select="search($item)"
This is my search() function
$scope.search = function() {
console.log($scope.searchTerms);
$scope.currentPage = 0;
$scope.results.documents = [];
$scope.isSearching = true;
return searchService.search($scope.searchTerms, $scope.currentPage).then(function(es_return) {
var totalItems = es_return.hits.total;
var totalTime = es_return.took;
var numPages = Math.ceil(es_return.hits.total / $scope.itemsPerPage);
$scope.results.pagination = [];
for (var i = 1; i <= 100; i++) {
if(totalItems > 0)
$scope.results.totalItems = totalItems;
$scope.results.queryTime = totalTime;
$scope.results.pagination = searchService.formatResults(es_return.hits.hits);
$scope.results.documents = $scope.results.pagination.slice($scope.currentPage, $scope.itemsPerPage);
}
}
),
function(error){
console.log('ERROR: ', error.message);
$scope.isSearching = false;
}
};
I'm not quite sure what is wrong? I'm thinking it has something to do with $scope, but I'm not sure. The query works when I use it Sense plugin for ES and it also works if I just type in a search term instead of selecting it from the autocomplete dropdown.
If it is $scope, what am I missing?
UPDATE
All shards failed for phase: [query_fetch]
org.elasticsearch.search.SearchParseException: [hugetestindex][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{"query":{"multi_match":{"query":{"_index":"hugetestindex","_type":"doc","_id":"57","_score":3.877801,"_source":{"ymme":"bourne supremacy"}},"fields":["content","title"],"operator":"and"}},"size":100,"from":0,"highlight":{"fields":{"title":{"number_of_fragments":0},"content":{"number_of_fragments":10,"fragment_size":300}}}}]]
UPDATE 2 Object {_index: "hugetestindex", _type: "doc", _id: "56", _score: 2.5276248, _source: Object}
I think that is the problem, instead of a search terms, its receiving "Object"....?
UPDATE 3So basically it goes like this,
[Object, Object, Object, Object, Object]
0: Object
_id: "229"
_index: "hugetestindex"
_score: 3.3071127
_source: Object
ymme: "bourne supremacy"
__proto__: Object
_type: "doc"
__proto__:
Object1:
Object2:
Object3:
Object4:
Object
length: 5
__proto__: Array[0]
where "bourne supremacy" is the value of the ymme field in the _source Object, the array at the top with the 5 objects is the es return, es_return.hits.hits - this last hits, is the array.
The way you deconstruct your object is by doing something like the following:
object.data.hits.hits._source.field_name;
The above is only a notation to get the value of a single field, you might need to do a loop for each of those values so maybe something like:
$scope.list = []
for hit in object.data.hits.hits {
$scope.list.push(hit._source.field);
}
console.log(list);
Then from your HTML you want to use this list by doing an ng-repeat with it or something similar to get each of the terms in the list.
<div ng-repeat="term in list">{{term}}</div>
If you can update your question with how your object looks and what data you want from it, I can update this answer to match it exactly.
UPDATE
To match your data structure, I'm assuming you want to extract each of the ymme values from those objects. You need to do the following:
<div ng-repeat="object in es_return.hits.hits">
{{object._source.ymme}}
</div>
Just make sure "es_return" is a $scope variable so you can access it as above or just do:
$scope.es_return = es_return;
In your Angular code

Array<object> in typescript

I wanna add an object to an array but I don't know how to declare a variable with the return type is Array<object>.
My example:
var obj = {
'Prop name': 'Prop value'
};
document.body.innerHTML = typeof obj; // output: object
var arr: Array<object>; // error message: Cannot find name 'object'
arr.push(obj);
I've tried again with:
var obj: Object = {
'Prop name': 'Prop value'
};
document.body.innerHTML = typeof obj; // output: object
var arr: Array<Object>;
arr.push(obj); // error message: Cannot read property 'push' of undefined
How can I fix the issue?
The first error is because it's Object (title case) not object (lower case)
The second error is because you've typed the variable, but not actually assigned an instance. Try:
var arr: Array<Object> = [];
arr.push(obj);
You miss initialization of arr array:
var obj: Object = {
'Prop name': 'Prop value'
};
document.body.innerHTML = typeof obj; // output: object
var arr: Array<Object> = []; // MODIFIED
arr.push(obj);
You should use Object or any instead:
var arr: Array<any>;

Access Nested Backbone Collection

Below is what i am populating my collection with (FacetModels)
How do I access the AvailableValues[] array
"FacetModels":[
{
"FacetDisplayLabel":null,
"SelectedValues":[],
"AvailableValues":[],
"UnknownResults":0,
"ActionURI":null,
"FacetGroupName":"Category",
"FacetGroupFriendlyId":"SourceCategory",
"FacetGroupOrder":10,
"AllowSuggestions":false
},
This is my view, as you will see all i have access to is the array of FacetModels, I need to be able to pass FacetModels[0].AvailableValues.Name so I can display each category Name
CategoryListItemView = Backbone.View.extend({
tagName: "li",
className: "category",
initialize: function (options) {
this.template = _.template( $("#categorylist_template").html());
},
render: function () {
var category = this.model
console.log(category);
console.log(this.model.toJSON());
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
Display from console
a.Model {cid: "c2", attributes: Object, collection: r, _changing: false, _previousAttributes: Object…}
_changing: false
_events: Object
_pending: false
_previousAttributes: Object
attributes: Object
ActionURI: null
AllowSuggestions: false
AvailableValues: Array[8]
0: Object
ActionURI: "/api/search?firstname=thomas&firstname_variants=true&lastname=smith&region=all&sourcecategory=armed%20forces%20utf0026%20conflict"
Count: 8943
DisplayLabel: "Armed Forces & Conflict"
IsUnknown: false
Name: "Armed Forces & Conflict"
proto: Object
1: Object
2: Object
3: Object
4: Object
5: Object
6: Object
7: Object
length: 8
proto: Array[0]
FacetDisplayLabel: null
FacetGroupFriendlyId: "SourceCategory"
FacetGroupName: "Category"
FacetGroupOrder: 10
SelectedValues: Array[0]
UnknownResults: 0
proto: Object
changed: Object
cid: "c2"
collection: r
proto: Object
Inside your view the javascript array is available through this.model.get('AvailableValues'). If you need Available values to be a Backbone Collection, you can override parse to populate the AvailableValues property with a collection instead of an array.
There are some other SO questions that have examples of this:
backbone-js-fetching-a-more-complex-data-and-using-as-a-collection
how-to-override-backbones-parse-function

In Backbone Relational how do I silence add:* events on fetch()

When I load in my model using .fetch() backbone Backbone Relational fires off an add:survey_question for each question it adds (which is the same thing that would happen if load something onto my survey_questions collection via .add(). Even worse, the add:survey_questions dont' respect the comparator on my survey_questions collections.
I would like a way to silence the add:survey_questions that happen
Console.log output when listening to all events on the Survey Model (SurveyBuilder) and Question model (embedded inside a SurveyQuestion model)
//START of .fetch() on the Survey Model
SurveyBuilder
["relational:change:survey_questions", child, Array[3], Object]
app.js:25338
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
Question
["add:survey_question", child, Backbone.Collection, Object]
app.js:25575
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
Question
["add:survey_question", child, Backbone.Collection, Object]
app.js:25575
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
Question
["add:survey_question", child, Backbone.Collection, Object]
app.js:25575
SurveyBuilder
["sync", child, Object, Object]
app.js:25338
//START OF Manual .add()
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
SurveyQuestion
["change:question_number", child, 4, undefined]
app.js:25578
SurveyQuestion
["change", child, undefined]
app.js:25578
And here are my models:
m.Question = m.BaseModel.extend({
urlRoot: '/api/questions',
initialize: function(){
if(this['type']) {
this.set('type', this['type']);
} else if(this.get('type')) {
this.type = this.get('type');
}
m.BaseModel.prototype.initialize.apply(this, arguments);
},
defaults: {
question_text: '',
question_number: 1,
comment_field: true
},
calculateProperties: function(){
return {
'question_id': this.getID()
};
},
subModelTypes: {
'Numeric': 'APP.Models.NumericQuestion',
'MultipleChoice': 'APP.Models.MultipleChoiceQuestion',
'Checkboxes': 'APP.Models.CheckboxesQuestion',
'FreeText': 'APP.Models.FreeTextQuestion',
'TimePeriod': 'APP.Models.TimePeriodQuestions',
'Sorting': 'APP.Models.SortingQuestion',
'Grading': 'APP.Models.GradingQuestion',
'Slider': 'APP.Models.SliderQuestion',
'AgreeDisagree': 'APP.Models.AgreeDisagreeQuestion',
'YesNo': 'APP.Models.YesNoQuestion'
}
});
m.SurveyQuestion = m.BaseModel.extend({
defaults: {
'question_number': 1
},
relations: [{
type: Backbone.HasOne,
key: 'question',
relatedModel: 'APP.Models.Question',
includeInJSON: '_id',
reverseRelation: {
type: Backbone.HasMany,
key: 'survey_question',
includeInJSON: false
}
}]
});
m.Survey = m.BaseModel.extend({
urlRoot: '/api/surveys',
defaults: {
start_date: moment().format('YYYY-MM-DD'),
end_date: moment().add('weeks', 3).format('YYYY-MM-DD'),
title: moment().format('MMMM') + ' Economist Survey'
},
relations: [{
type: Backbone.HasMany,
key: 'survey_questions',
relatedModel: 'APP.Models.SurveyQuestion',
collectionType: 'APP.Collections.SurveyQuestions',
includeInJSON: true,
reverseRelation: {
key: 'survey',
includeInJSON: true
}
}]
});

Resources