I have the following string comming from server (as part of object):
...
SelectValues: "[{displayName: "Not selected", id: 0},{displayName: "Client", id: 1},{displayName: "Industry", id: 2},{displayName: "Country", id: 3}]"
...
I am using mapping pluging:
var ItemModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
}
As a result all items are created properly, except SelectValues. SelectValues should be observable array (each array item should have two properties id & displayName). Later I will use SelectValues for dropdown.
The problem is that it is returned as String and not as Array.
The question is whether I can somehow deal with it on client side (without changing format on server side)?
I probably can create SelectValuesComputed as ko.computed and somehow convert SelectValues to array (how?).
First of all, this is invalid JSON string you have in your property, see documentation (property names should be in quotes). But, having the condition that you cannot change the server code, you can use eval function to get your object and then use it in mapping like this:
var ItemModel = function (data) {
var self = this;
var mapping = {
"SelectValues": {
create: function(options){
return ko.mapping.fromJS(eval(options.data));
}
}
};
ko.mapping.fromJS(data, mapping, self);
}
See working demo. Although this might seem like the simpliest way to workaround the problem, I would strongly recommend you though to get a valid JSON string instead of using eval (or regexp) to get your object.
Related
I'm trying to build a news reader app and currently have an Article object that returns an array:
class Article: NSObject, Mappable {
var author: String?
var title: String?
var desc: String?
var url: String?
var urlToImage: String?
var publishedDate: String?
var sent = false
required init?(map: Map){
}
I have added a boolean called Sent to keep track of whether the user has seen the article before, however each time I fetch news data it will override the existing Article Array.
I have tried using a method for removing duplicates in an array, however this does not keep track of whether the user has seen the article already or not.
What is the best approach to solving this problem? I know I have to iterate through the array each time the api is called but is there a better approach than this?
let newArticles = [Article]()
for (index, newArticle) in newArticles.enumerated() {
if articles[index].title != newArticle.title {
articles.append(newArticle)
}
}
I would first recommend that all boolean variables start with a conditional word "is", "has", or "was". Secondly it sounds like your array would be perfect as an OrderedSet rather than and Array. An OrderedSet is an order collection of values that does not allow multiple entries of the same object. So I would have a Set (NSSet) of Article objects that have not yet been seen. Once the user has seen that Article mark it's isSent property as true. All other Articles in the Set that has not yet been seen yet will have it's isSent property to false.
And maybe change the wordage of your boolean to be "isSeen" instead of "isSent".
EDIT
let set = NSOrderedSet(objects: articles)
if let setArray = set.array as? [Any] {
let filteredSet = setArray.filter({ (article) -> Bool in
return article.isSeen == true
})
}
I have a dictionary of type {name: value}
A = {
name: x,
name: y,
name: z
}
I want to fetch a collection (consisting of models having one of their attribute as 'name'), but to be optimal I want to fetch such that the value of the attribute 'name' exists in my dictionary.
Is there a way to do specific filtering like that?
If you're doing the filtering client-side, overriding the filter method is really NOT the way to go.
Now you no longer have it available, should you need it later. Also, modifying the collection itself from within the filter method is an undesirable sideeffect.
Instead you should be using the parse method, which will automatically be called when fetching the collection.
Now as I understand it, you want to limit the fetched set to models with names matching the keys in your dictionary.
If so, I would do the following:
parse: function(response, options) {
// Do we want to filter the response?
if (options.filterNames) {
// Filter
response = _.filter(response, function(obj) {
// Check if this model name is one of the allowed names
return _.contains(options.filterNames, obj.name);
});
}
// Backbone will use the return value to create the collection
return response;
}
And then call fetch using
someCollection.fetch({filterNames: _.keys(someDictionary)});
If you're certain, you will always be filtering the collection on fetch, you can omit passing the option and just use the dictionary within parse.
Alternatively you could create a fetchFiltered() method on the collection, which would then invoke the line above.
After investigations and trials, here are the two ways this can be resolved:
1. Client side filtering after fetching the collection from the server. This is a less optimal method, especially when the collection is huge. In situations when you really want 5 models out of a 1000 model collection, it can be an overkill. But if the server side has no logic of accepting and using the filtering client side filtering should look something like:
Overload the collection filter code something like:
var filter = {
filter: function() {
var results = _.filter(this.models, function(model) {
// Perform the check on this model, like compare it to your local dict
if (checkPassed) {
return true;
}
return false;
});
results = _.map(results, function(model) {
return model.toJSON();
});
// Reset the existing collection to filtered models
this.reset(results) ;
};
var ExtendedCollection = OriginalCollection.extend(filter);
Pass a filter option in the fetch ajax call to the server, and the server should understand the filter and return the collection based off that.
i am new to AngularJs. I am trying to get the values from json based on search key word and displaying that related data.
Below is my json format
{"abc":[
{ "url":"abc.json"}
],
"bbc":[
{"url":"bbc.json"}
]
}
i am comparing the key with entered search word. If it matches then i need to load the separate json which is related to found key. I am able to compare the search word with key, but when i am trying to get the value am getting total value like url=abc.json, instead of this i've to get only abc.json. My controller code is as follows
function getDetailsController($scope,$http) {
$scope.search = function() {
$http.get("searchInfo.json").success(function(response) {
angular.forEach(response, function(value, key) {
if(angular.equals(key, $scope.searchText)){
$scope.url = value;
$http.get(url).success(function(response) {
$scope.details = response;
});
}
});
});
};
}
I have tried in diffrent ways but i was not able to get the value. Could you please help me on this.
In your JSON, '"abc"': is a key, and its corresponding value is
[{ "url":"abc.json"}]
That is an array containing a single object, the object containing a single key: "url".
So, to access this URL, you simply need
value[0].url
This structure is quite strange though. I don't really understand the point of wrapping the object inside an array.
According to the Angularfire docs, when working with an object returned through $asObject(), you can set priority for said object by defining a $priority property on the object and then using $save().
My code works great, but $priority isn't doing anything. Here's some code with complete explanations in the comments:
app.factory('MyService', function($rootScope, $firebase) {
// The complete Firebase url
var ref = *obfuscated*;
// Returning the dataset as an object containing objects
var data = $firebase(ref).$asObject;
// This object is what's returned by MyService
var Data = {
// Method to create a new object within the data set,
// keyed by myId. Attempting to set priority for the
// record via $priority. returnData.uid is a valid string.
create: function(returnData, myId) {
data[myId] = {
myId: myId,
$priority: returnData.uid
};
// No need to explain the stuff between here and the
// $rootScope listener below, just added for context
data.$save().then(function() {
setMyId(myId);
});
},
findByMyId: function(myId) {
if (myId) {
return data[myId];
}
}
};
function setMyId(myId) {
$rootScope.myId = User.findByMyId(myId);
}
// This event listener works fine, fires
// at user login and returns data
$rootScope.$on('$firebaseSimpleLogin:login', function(e, returnData) {
// returnData.uid has the correct value - this
// function should return the object(aka record) with
// a priority matching returnData.uid
var query = $firebase(ref.startAt(returnData.uid).endAt(returnData.uid)).$asObject();
// console shows an object with the normal $firebase
// properties, but no records. If I define query without
// limiting the set ($firebase(ref).$asObject()), it returns
// the full set just fine. The priority on all records is still
// null.
console.log(query);
query.$loaded(function() {
setData(query.myId);
});
});
return Data;
});
Yes, I'm following Thinkster.io's tutorial and I'm in Chapter 7. No, this is not a duplicate of the other questions about that chapter, I already found my way around the pre-Angularfire 0.8 code present in their examples, just can't set $priority, and I've spent about 5 hours so far trying to find a solution through my own efforts and on the web.
Any takers?
When viewed in the light of how JavaScript works with objects (i.e. unordered), how JSON handles objects (i.e. unordered), and in light of the expectation that AngularFire's $asObject() method is intended for storing key/value pairs, and singular records that are not used as a collection, this starts to make some sense.
Internally, the synchronize'd object's $save method calls Firebase's setWithPriority. In set or setWithPriority calls, the child nodes are replaced. Any meta data like priorities on those children are replaced.
In AngularFire, $asArray is intended to handle ordered collections, and provides the ability to set $priority on child nodes (only one level deep, of course, as it treats its children as singular records that are not themselves collections).
Since, in your case, you want to work with fixed keys rather than push ids, you'll probably want to override the $add method using $extendFactory and do something like the following:
angular.module(MY_APP).factory('FixedKeysFactory', function($FirebaseArray, $firebaseUtils) {
return $FirebaseArray.$extendFactory({
$add: function(data) {
this._assertNotDestroyed('$add');
if( angular.isObject(data) && typeof data.$id === 'string' ) {
return this.$inst().$set(data.$id, $firebaseUtils.toJSON(data));
}
else {
return this.$inst().$push($firebaseUtils.toJSON(data));
}
}
});
});
You could then pass this into your $firebase instance in place of the default factory:
var list = $firebase(ref, {arrayFactory: FixedKeysFactory}).$asArray();
A simpler but less awesomatic™ solution would be to manually add your objects to the array, manually giving them a $id, then call $save:
var list = $firebase(ref).$asArray();
var i = list.length;
list.push({ foo: 'bar', $id: 'kato' });
list.$save(i);
Some notes on the future: It will soon be possible to use any field as sort criteria and there will be no need to set priorities (yay!). It will probably be possible to set your own $id before calling $add on a synchronized array in AngularFire as soon as I clear that with the other devs (like the 0.8.3 release).
now that I managed to get random AJAX output, I want to get some useful values from the database as the next step.
Once again, my AJAX call looks like this (additionally, I added a JSON call, which would be even better).
$.ajax({
url: "index.php",
data: "tx_myext_myplugin1[controller]=Mycontroller1&tx_myext_myplugin1[action]=ajax&type=89657201",
success: function(result) {
alert(result);
}
});
/*
var uri = '<f:uri.action action="ajax" controller="Mycontroller1" pageType="89657201" />';
jQuery.getJSON(uri, function(result) {
alert(result.c);
});
*/
my ajaxAction function:
public function ajaxAction() {
$arr = array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
return json_encode($arr);
}
This works when I use the JSON call, now I need to get an array with database values though. I thought calling the repository with a findAll() function would already help, but it's not an array I think, which is why I can't use it. A different idea would be to use the getValue methods I wrote in the Model, but I'm not sure if this would help.
Disclaimer: Generally using findAll() method can be real performance killer therefore try to write custom finders, selecting only required properties, especcialy if your model is big or contains many relations!
You are close enough, as you can send findAll() result with json_encode(), but be careful, depending on your model, json created from findAll can be really huge. It's better idea to iterate results and rewrite to new array only required values.
$dataFromRepo = $this->yourRepository->findAll();
$resultArray = array();
foreach ($dataFromRepo as $object){
$resultArray[$object->getUid()] = $object->getTitle();
}
return json_encode($resultArray);
in result you'll get basic JSON object:
{
"1": "Title of first item",
"2": "Second item",
"3": "Et cetera"
}
When you'll remove custom index from $resultArray
foreach ($dataFromRepo as $object){
$resultArray[] = $object->getTitle();
}
you will get JSON array
[
"Title of first item",
"Second item",
"Et cetera"
]
And so on. Of course you can also build this way multidimensional array and send more sophisticated objects to get all you need at once.
P.S. Try always to use for an example JsonLint - online validator to validate if the output you're expecting is valid.