I'm pretty puzzled by the way reactfire works, as there doesn't seem to much about it documentation-wise.
So I want to delete and update some child nodes, but I have no idea how to do it. All tutorials focus solely in retrieving data, which are treated as a regular array and I don't even seem to get access to their keys.
Here's the official example: https://www.firebase.com/blog/2014-05-01-using-firebase-with-react.html
How do perform these operations using React?
Once you instantiate FB:
```
this.fb = new Firebase('https://react-testing.firebaseio.com/items');
this.bindAsArray(this.fb, 'items');
```
'items' becomes bound to this.state.items. Cool, now I have the data.
But how do I manipulate it? What's the correct way of getting a reference to the item being passed?
As I said in my comment: ReactFire is a tiny wrapper around a small subset of the regular Firebase JavaScript SDK. If you want to build an application beyond its capabilities, you can easily expand on it.
For your request I went ahead and changed the following snippet in ReactFire:
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
out.push(obj[key]);
}
}
To this:
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var item = obj[key];
item['$id'] = key;
out.push(item);
}
}
So we now pass the key() of each item as a "special" $id property of the item in the array.
With that I can expand the TodoList3 class of the original to this:
var TodoList3 = React.createClass({
handleClick: function(key) {
if (this.props.onClick) {
this.props.onClick(key);
}
},
render: function() {
var list = this;
var createItem = function(item) {
var boundClick = list.handleClick.bind(list, item['$id']);
return <li key={ item['$id'] } onClick={boundClick}>{ item.text }</li>;
};
return <ul>{ this.props.items.map(createItem) }</ul>;
}
});
So we now identify the Todo items by their $id/key(), instead of their index and use that value when the user clicks the item.
With that we can expand the JSX of TodoApp3 to pass in a handler for when the user clicks an item:
<TodoList3 items={ this.state.items } onClick={this.handleClick} />
And the app will then delete the item, by calling into the regular Firebase JavaScript SDK.
handleClick: function(key) {
var firebaseRef = this.state.ref;
firebaseRef.child(key).remove();
},
Links:
my modified ReactFire script
a fiddle showing the complete code in action
Related
I'm trying to find the most "knockout" way of doing this.
I've got a list of files that are fetched via a GET request and are stuffed into an array in my template's view model. I iterate through those in the template and create a <partial-li-file> for each item.
I'm trying to filter the files from a searchbox. I was able to do this in a previous iteration where PartialLiFile objects were held in the array but now it's just a list of Objects.
I don't see where or how I can access the PartialLiFile objects to filter them.
Is it even possible to do this like I've got it implemented here?
Template
<ul data-bind="foreach:files">
<partial-li-file params="{fil:$data}" data-bind="visible:!filtered()"></partial-li-file>
</ul>
Main ViewModel
self.search = ko.observable();
self.search.subscribe(function(text) {
// TODO: Need to filter the files here
});
// Fetch the file data
self.files = ko.observableArray();
$.getJSON("get/files/", function(data) {
data.map(function(fil) {
self.files.push(fil);
});
});
Partial ViewModel
function PartialLiFile(params) {
var self = this;
ko.mapping.fromJS(params.fil, {}, self);
// TODO: need to set this to true if the name matches the search string
self.filtered = ko.observable(false);
}
After some trial and error I've figured out the answer. I added the parent's search observable, $parent.search, to the list of parameters passed into the component and subscribed to it int he partial's view model. I didn't realize that it was possible to share an observable in this way.
Template
<ul data-bind="foreach:files">
<partial-li-file params="{fil:$data,search:$parent.search}" data-bind="visible:!filtered()"></partial-li-file>
</ul>
Partial ViewModel
function PartialLiFile(params) {
var self = this;
ko.mapping.fromJS(params.fil, {}, self);
self.search = params.search;
self.search.subscribe(function(search) {
self.filtered(self.name().search(new RegExp(search, "i")) == -1);
});
self.filtered = ko.observable(false);
}
I'm trying to make the {{#each}} helper to iterate over an object, like in vanilla handlebars. Unfortunately if I use #each on an object, Ember.js version gives me this error:
Assertion failed: The value that #each loops over must be an Array. You passed [object Object]
I wrote this helper in attempt to remedy this:
Ember.Handlebars.helper('every', function (context, options) {
var oArray = [];
for (var k in context) {
oArray.push({
key : k,
value : context[k]
})
}
return Ember.Handlebars.helpers.each(oArray, options);
});
Now, when I attempt to use {{#every}}, I get the following error:
Assertion failed: registerBoundHelper-generated helpers do not support use with Handlebars blocks.
This seems like a basic feature, and I know I'm probably missing something obvious. Can anyone help?
Edit:
Here's a fiddle: http://jsfiddle.net/CbV8X/
Use {{each-in}} helper. You can use it like like {{each}} helper.
Example:
{{#each-in modelWhichIsObject as |key value|}}
`{{key}}`:`{{value}}`
{{/each-in}}
JS Bin demo.
After fiddling with it for a few hours, I came up with this hacky way:
Ember.Handlebars.registerHelper('every', function(context, options) {
var oArray = [], actualData = this.get(context);
for (var k in actualData) {
oArray.push({
key: k,
value: actualData[k]
})
}
this.set(context, oArray);
return Ember.Handlebars.helpers.each.apply(this,
Array.prototype.slice.call(arguments));
});
I don't know what repercussions this.set has, but this seems to work!
Here's a fiddle: http://jsfiddle.net/CbV8X/1/
I've been after similar functionality, and since we're sharing our hacky ways, here's my fiddle for the impatient: http://jsfiddle.net/L6axcob8/1/
This fiddle is based on the one provided by #lxe, with updates by #Kingpin2k, and then myself.
Ember: 1.9.1, Handlebars: 2.0.0, jQuery 2.1.3
Here we are adding a helper called every which can iterate over objects and arrays.
For example this model:
model: function() {
return {
properties: {
foo: 'bar',
zoo: 'zar'
}
};
}
can be iterated with the following handlebars template:
<ul class="properties">
{{#every p in properties}}
<li>{{p.key}} : {{p.value}}</li>
{{/every}}
</ul>
every helper works by creating an array from the objects keys, and then coordinating changes to Ember by way of an ArrayController. Yeah, hacky. This does however, let us add/remove properties to/from an object provided that object supports observation of the [] property.
In my use case I have an Ember.Object derived class which notifies [] when properties are added/removed. I'd recommend looking at Ember.Set for this functionality, although I see that Set been recently deprecated. As this is slightly out of this questions scope I'll leave it as an exercise for the reader. Here's a tip: setUnknownProperty
To be notified of property changes we wrap non-object values in what I've called a DataValueObserver which sets up (currently one way) bindings. These bindings provide a bridge between the values held by our internal ArrayController and the object we are observing.
When dealing with objects; we wrap those in ObjectProxy's so that we can introduce a 'key' member without the need to modify the object itself. Why yes, this does imply that you could use #every recursively. Another exercise for the reader ;-)
I'd recommend having your model be based around Ember.Object to be consistent with the rest of Ember, allowing you to manipulate your model via its get & set handlers. Alternatively, as demonstrated in the fiddle, you can use Em.Get/Em.set to access models, as long as you are consistent in doing so. If you touch your model directly (no get/set), then every won't be notified of your change.
Em.set(model.properties, 'foo', 'asdfsdf');
For completeness here's my every helper:
var DataValueObserver = Ember.Object.extend({
init: function() {
this._super();
// one way binding (for now)
Em.addObserver(this.parent, this.key, this, 'valueChanged');
},
value: function() {
return Em.get(this.parent, this.key);
}.property(),
valueChanged: function() {
this.notifyPropertyChange('value');
}
});
Handlebars.registerHelper("every", function() {
var args = [].slice.call(arguments);
var options = args.pop();
var context = (options.contexts && options.contexts[0]) || this;
Ember.assert("Must be in the form #every foo in bar ", 3 == args.length && args[1] === "in");
options.hash.keyword = args[0];
var property = args[2];
// if we're dealing with an array we can just forward onto the collection helper directly
var p = this.get(property);
if (Ember.Array.detect(p)) {
options.hash.dataSource = p;
return Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
}
// create an array that we will manage with content
var array = Em.ArrayController.create();
options.hash.dataSource = array;
Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
//
var update_array = function(result) {
if (!result) {
array.clear();
return;
}
// check for proxy object
var result = (result.isProxy && result.content) ? result.content : result;
var items = result;
var keys = Ember.keys(items).sort();
// iterate through sorted array, inserting & removing any mismatches
var i = 0;
for ( ; i < keys.length; ++i) {
var key = keys[i];
var value = items[key];
while (true) {
var old_obj = array.objectAt(i);
if (old_obj) {
Ember.assert("Assume that all objects in our array have a key", undefined !== old_obj.key);
var c = key.localeCompare(old_obj.key);
if (0 === c) break; // already exists
if (c < 0) {
array.removeAt(i); // remove as no longer exists
continue;
}
}
// insert
if (typeof value === 'object') {
// wrap object so we can give it a key
value = Ember.ObjectProxy.create({
content: value,
isProxy: true,
key: key
});
array.insertAt(i, value);
} else {
// wrap raw value so we can give it a key and observe when it changes
value = DataValueObserver.create({
parent: result,
key: key,
});
array.insertAt(i, value);
}
break;
}
}
// remove any trailing items
while (array.objectAt(i)) array.removeAt(i);
};
var should_display = function() {
return true;
};
// use bind helper to call update_array if the contents of property changes
var child_properties = ["[]"];
var preserve_context = true;
return Ember.Handlebars.bind.call(context, property, options, preserve_context, should_display, update_array, child_properties);
});
Inspired by:
How can I make Ember.js handlebars #each iterate over objects?
http://mozmonkey.com/2014/03/ember-getting-the-index-in-each-loops/
https://github.com/emberjs/ember.js/issues/4365
https://gist.github.com/strathmeyer/1371586
Here's that fiddle again if you missed it:
http://jsfiddle.net/L6axcob8/1/
Just found out, that in KnockoutJS subscription functions are evaluated before dependent computables and need someone who can commit that, because I can't find anything about Knockouts timing in the docs or discussion forums.
That means: If I have a model like this...
var itemModel = function (i) {
var self = this;
self.Id = ko.observable(i.Id);
self.Title = ko.observable(i.Title);
self.State = ko.observable(i.State);
};
var appModel = function () {
var self = this;
self.Items = ko.observableArray() // <-- some code initializes an Array of itemModels here
self.indexOfSelectedItem = ko.observable();
self.selectedItem = ko.computed(function () {
if (self.indexOfSelectedItem() === undefined) {
return null;
}
return self.Items()[self.indexOfSelectedItem()];
});
};
where I want to keep track of the selected array item with an observable index field, and I subscribe to this index field like this...
appModel.indexOfSelectedItem.subscribe(function () {
// Do something with appModel.selectedItem()
alert(ko.toJSON(appModel.selectedItem()));
}
...the subscription function is evaluated before the computed is reevaluated with the new index value, so I will get the selectedItem() that corresponds to the last selected Index and not the actual selected Index.
Two questions:
Is that right?
Then why should I make use of ko.computed() if a simple function gets me the current selected Item every time I call it, while ko.computed gets evaluated at anytime where everything is done already and I dont need it anymore?
By default all computeds in Knockout are evaluated in an eager fashion, not lazily (i.e., not when you first access them).
As soon as one dependency changes, all all subscriptions are notified and all connected computeds are re-evaluated. You can change that behavior to "lazy" by specifying the deferEvaluation option in a computed observable, but you cannot do this for a subscription.
Hoewever, I think there is no need to depend on the index of the selected item. In fact, that's even bad design because you are not really intested in the numerical value of the index, but rather in the item it represents.
You could reverse the dependencies by creating a writeable computed observable that gives you the index of the currently selected item (for diplay purposes) and allows to change it as well (for convenience).
function AppModel() {
var self = this;
self.Items = ko.observableArray();
self.selectedItem = ko.observable();
self.indexOfSelectedItem = ko.computed({
read: function () {
var i,
allItems = self.Items(),
selectedItem = self.selectedItem();
for (i = 0; i < allItems.length; i++) {
if (allItems[i] === selectedItem) {
return i;
}
}
return -1;
},
write: function (i) {
var allItems = self.Items();
self.selectedItem(allItems[i]);
}
});
}
Knockout favors storing/handling the actual values instead of just indexes to values, so it would probably not be difficult to make the necessary changes to your view. Just make everything that previously wrote to indexOfSelectedItem now write to selectedItem directly. Dependencies on selectedItem will continue to work normally.
In a well-designed Knockout application you will rarely ever have the need to handle the index of an array item. I'd recommend removing the write part of the computed once everything works.
See: http://jsfiddle.net/4hLLn/
I am brand new to AngularJS and like what I've seen so far, especially the model / view binding. I'd like to make use of that to construct a simple "add to basket" piece of functionality.
This is my controller so far:
function BasketController($scope) {
$scope.products = [];
$scope.AddToBasket = function (Id, name, price, image) {
...
};
}
And this is my HTML:
<a ng-click="AddToBasket('237', 'Laptop', '499.95', '237.png')">Add to basket</a>
Now this works but I highly doubt this is the right way to create a new product object in my model. However this is where my total lack of AngularJS experience comes into play.
If this is not the way to do it, what is best practice?
You could use ng-init in an outer div:
<div ng-init="param='value';">
<div ng-controller="BasketController" >
<label>param: {{value}}</label>
</div>
</div>
The parameter will then be available in your controller's scope:
function BasketController($scope) {
console.log($scope.param);
}
You could create a basket service. And generally in JS you use objects instead of lots of parameters.
Here's an example: http://jsfiddle.net/2MbZY/
var app = angular.module('myApp', []);
app.factory('basket', function() {
var items = [];
var myBasketService = {};
myBasketService.addItem = function(item) {
items.push(item);
};
myBasketService.removeItem = function(item) {
var index = items.indexOf(item);
items.splice(index, 1);
};
myBasketService.items = function() {
return items;
};
return myBasketService;
});
function MyCtrl($scope, basket) {
$scope.newItem = {};
$scope.basket = basket;
}
I'm not very advanced in AngularJS, but my solution would be to use a simple JS class for you cart (in the sense of coffee script) that extend Array.
The beauty of AngularJS is that you can pass you "model" object with ng-click like shown below.
I don't understand the advantage of using a factory, as I find it less pretty that a CoffeeScript class.
My solution could be transformed in a Service, for reusable purpose. But otherwise I don't see any advantage of using tools like factory or service.
class Basket extends Array
constructor: ->
add: (item) ->
#push(item)
remove: (item) ->
index = #indexOf(item)
#.splice(index, 1)
contains: (item) ->
#indexOf(item) isnt -1
indexOf: (item) ->
indexOf = -1
#.forEach (stored_item, index) ->
if (item.id is stored_item.id)
indexOf = index
return indexOf
Then you initialize this in your controller and create a function for that action:
$scope.basket = new Basket()
$scope.addItemToBasket = (item) ->
$scope.basket.add(item)
Finally you set up a ng-click to an anchor, here you pass your object (retreived from the database as JSON object) to the function:
li ng-repeat="item in items"
a href="#" ng-click="addItemToBasket(item)"
Hi I'm trying to author a jQuery plugin and I need to have methods accessible to elements after they are initialized as that kind of object, e.g.:
$('.list').list({some options}); //This initializes .list as a list
//now I want it to have certain methods like:
$('.list').find('List item'); //does some logic that I need
I tried with
$.fn.list = function (options) {
return this.each(function() {
// some code here
this.find = function(test) {
//function logic
}
}
}
and several other different attempts, I just can't figure out how to do it.
EDIT:
I'll try to explain this better.
I'm trying to turn a table into a list, basically like a list on a computer with column headers and sortable items and everything inbetween. You initiate the table with a command like
$(this).list({
data: [{id: 1, name:'My First List Item', date:'2010/06/26'}, {id:2, name:'Second', date:'2010/05/20'}]
});
.list will make the <tbody> sortable and do a few other initial tasks, then add the following methods to the element:
.findItem(condition) will allow you to find a certain item by a condition (like findItem('name == "Second"')
.list(condition) will list all items that match a given condition
.sort(key) will sort all items by a given key
etc.
What's the best way to go about doing this?
If you want these methods to be available on any jQuery object, you will have to add each one of them to jQuery's prototype. The reason is every time you call $(".list") a fresh new object is created, and any methods you attached to a previous such object will get lost.
Assign each method to jQuery's prototype as:
jQuery.fn.extend({
list: function() { .. },
findItem: function() { .. },
sort: function() { .. }
});
The list method here is special as it can be invoked on two occasions. First, when initializing the list, and second when finding particular items by a condition. You would have to differentiate between these two cases somehow - either by argument type, or some other parameter.
You can also use the data API to throw an exception if these methods are called for an object that has not been initialized with the list plugin. When ('xyz').list({ .. }) is first called, store some state variable in the data cache for that object. When any of the other methods - "list", "findItem", or "sort" are later invoked, check if the object contains that state variable in its data cache.
A better approach would be to namespace your plugin so that list() will return the extended object. The three extended methods can be called on its return value. The interface would be like:
$('selector').list({ ... });
$('selector').list().findOne(..);
$('selector').list().findAll(..);
$('selector').list().sort();
Or save a reference to the returned object the first time, and call methods on it directly.
var myList = $('selector').list({ ... });
myList.findOne(..);
myList.findAll(..);
myList.sort();
I found this solution here:
http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html
This seems to do exactly what I need.
(function($) {
var TaskList = function(element, options)
{
var $elem = $(element);
var options = $.extend({
tasks: [],
folders: []
}, options || {});
this.changed = false;
this.selected = {};
$elem.sortable({
revert: true,
opacity: 0.5
});
this.findTask = function(test, look) {
var results = [];
for (var i = 0,l = options.tasks.length; i < l; i++)
{
var t = options['tasks'][i];
if (eval(test))
{
results.push(options.tasks[i]);
}
}
return results;
}
var debug = function(msg) {
if (window.console) {
console.log(msg);
}
}
}
$.fn.taskList = function(options)
{
return this.each(function() {
var element = $(this);
if (element.data('taskList')) { return; }
var taskList = new TaskList(this, options);
element.data('taskList', taskList);
});
}
})(jQuery);
Then I have
$('.task-list-table').taskList({
tasks: eval('(<?php echo mysql_real_escape_string(json_encode($tasks)); ?>)'),
folders: eval('(<?php echo mysql_real_escape_string(json_encode($folders)); ?>)')
});
var taskList = $('.task-list-table').data('taskList');
and I can use taskList.findTask(condition);
And since the constructor has $elem I can also edit the jQuery instance for methods like list(condition) etc. This works perfectly.
this.each isn't needed. This should do:
$.fn.list = function (options) {
this.find = function(test) {
//function logic
};
return this;
};
Note that you'd be overwriting jQuery's native find method, and doing so isn't recommended.
Also, for what it's worth, I don't think this is a good idea. jQuery instances are assumed to only have methods inherited from jQuery's prototype object, and as such I feel what you want to do would not be consistent with the generally accepted jQuery-plugin behaviour -- i.e. return the this object (the jQuery instance) unchanged.