I have this following function in my controller:
$scope.add = function(newItem){
if (!(newItem.title && newItem.text)) return;
var sanitized = {
title: newItem.title,
text: newItem.text,
date: Firebase.ServerValue.TIMESTAMP,
};
// $rootScope.currentUser is here bound with $firebaseObject(ref).$bindTo($rootScope, 'currentUser').
$rootScope.currentUser.list.push(sanitized);
// .list is an array: [].
};
And I utilise this in my view like follows:
<form name="newUp">
<input ng-model="newItem.title">
<textarea ng-model="newItem.text"></textarea>
<button ng-click="add(newItem)">Submit</button>
</form>
<div ng-repeat="item in currentUser.list | orderBy:'-date'">
<p>
<span>{{item.title}} <small> - {{item.date | date:'d MMM yy'}}</small></span><br>
<span ng-if="item.text"><small>{{item.text}}</small></span>
</p>
</div>
However after the submit button is clicked, and the item is saved to the firebase database, it displays on screen as:
{".sv":"timestamp"}
After I refresh the page entirely, it shows the item(s) with the correct timestamp(s). Is there a way to avoid this? Can I avoid using $firebaseArray to fix this?
I am using bower which has pulled down the following versions:
AngularFire 1.1.2
Firebase v2.2.7
AngularJS v1.4.1
From a quick glance there is a bug in work of $firebaseObject. It's obvious that it doesn't treat properly value of a special object Firebase.ServerValue.TIMESTAMP which tells server to put the timestamp on server-side. However this is not the case when adding same object in to $firebaseArray with a synchronized method $add.
Perhaps I am missing some kind of flush method for $firebaseObject but I can't find it.
In any case you should use $firebaseArray because your data is of type Array. And don't forget to use $add, $save and $remove as per docs
Related
I am working on a Meteor application and am trying to pass an attribute of an item in a collection to a javascript function. In this instance I working with instafeed.js, a javascript plugin for the instagram API, but I would like to understand the process on a more fundamental level.
I’ve been able to pull records from my Teams collection into the /teams/:id route, and display attributes of a team using the {{name}} and {{igHandle}} template helpers.
I have also been able to get instafeed.js to work, using this package etjana:instafeed and the demo provided online. The tag I am pulling is assigned statically via:Session.setDefault(‘tagName’,’football’);
Eventually I would like to pull the user profiles, but that requires an access token from oauth2. I think that can be achieved with a {{$.Session.get access_token}} handlebar helper, but I need to figure out to feed a variable into the instafeed.js function first.
Could someone please explain how to pass the {{igHandle}} attribute through to the tagName in the javascript function. My code is as follows:
Team Template:
<template name="teamView">
<div class=“ui container”>
<h3>{{name}}</h3>
<p>
<i class="fa fa-instagram fa-lg"></i>
<a target="_blank" href="http://instagram.com/{{insta_hndl}}"> {{insta_hndl}}</a>
</p>
<br>
<h3>Instagrams</h3>
{{> igTeamFeed}}
</div>
</template>
Everything works, except the {{>igTeamFeed}} render. I am able to get content to show, but it is currently static. (assigned via (Session.setDefault('tagValue','football').
Instafeed Template:
<template name="igTeamFeed">
<div class="ui container”>
<h3>#{{insta_hndl}} Instagram</h3>
<div id="instafeed"></div>
</div>
</template>
Content is displaying, but again only through the static (Session.setDefault('tagValue','football') code.
Router:
Router.route('/teams/:_id', {
name: 'teamView',
template: 'teamView',
data: function(){
var currentTeam = this.params._id;
return Teams.findOne({ _id: currentTeam });
},
action: function() {
if (this.ready()) {
this.render('teamView');
} else {
this.render('loading');
}
}
});
Works with template helpers, so I am thinking I am ok here. Also following the instructions of a user per one of my prior posts.
Instafeed Javascipt: (needs some work)
Template.igTeamFeed.helpers ({
igData: function() {
return Session.get('insta_hndl');
},
});
Template.igTeamFeed.onRendered(function () {
//clear any previously stored data making the call
Session.get('insta_hndl'); //extra add-in
Session.set('insta_hndl', null);
Session.set('insta_hndl', this.data.insta_hndl); //extra add-in
var igHandle = this.data.insta_hndl;
});
Tracker.autorun(function(){
feed = new Instafeed({
get: 'tagged',
tagName: Session.get('insta_hndl'),
clientId: ‘xxxxxxxxxxxxxxxxxxx’,
resolution: 'thumbnail',
sortBy: 'most-liked',
limit: '15'
});
feed.run();
});
I have a helper to get the current insta_hndl for the session. The session item is passed through the team/:_id tag of the url, and defined in the router
The onRendered is wiping out the old handle, and inserting the new one. I added two additional Session.get and Session.set functions since I was getting an error that insta_hndl was undefined (differing from the response on my previous post). Could be wrong there.
The tracker.autorun function was code I had working with an instafeed example. Should it be somewhere else? Maybe in a helper or onRendered function? Also do I need tracker.autorun to use instafeed? To my understanding it dynamically updates the feed when a tagName changes, but aren't there other ways to do this?
How to Solve
These are some ways I'm thinking I could to solve this. Please advise on how to do this / what you think is best practice:
Collection Helpers: Was thinking I could call something like Teams.findOne().insta_hndl but didn't have much luck. Is that the right code block to use? Do I have to define a variable in the template helper, or can I call it directly in the feed js function?
Handlebars Helpers: Thinking I could do something with a Session helper, but not sure if it would work if one user had two different instances of the instafeed open (two tabs open with different teams selected).
Reactive Methods: Allows one to call methods synchronously inside Tracker.autorun. Thought i read something that said Meteor already has this functionality baked in, but please advise.
Iron Router Query: Part of me still isn't convinced the insta_hndl isn't getting passed through. I've explored adding the instagram handle as a query param to the URL, but do not think this is a best practice.
If someone could help me get this working that woudld be great, but explanations would even better! Really spending a lot of time on this, and many of the online resources are using depreciated syntax.
Also two more related questions questions:
Should I use a controller? Should i write out a controller separately? Or just include it in Router.route functions? Seeing some people rely heavily on controllers, but a lot of documentation does everything through Router.route.
Should I break out the instafeed function into a method? If so how should I do this? I spent a great amount of time tryig to set up the whole instafeed function as a server side method, but couldn't seem to get it to work. I only foresee myself using this function in one or two other templates, so I figured it was fine to leave as is. Please advice.
Sorry that this was a bit confusing. The correct javascript is:
Template.igTeamFeed.helpers ({
teams: function() {
return Teams.find();
}
});
Template.igTeamFeed.onCreated( function() {
var igHandle = Teams.findOne(Router.current().params._id).insta_hndl;
feed = new Instafeed({
get: 'tagged',
tagName: Session.get('insta_hndl'),
clientId: ‘xxxxxxxxxxxxxxxxxxx’,
resolution: 'thumbnail',
sortBy: 'most-liked',
limit: '15'
});
feed.run(); //run the new Instafeed({})
});
There is no need for any of the things I suggested. The attribute can be grabbed with Collection.findOne(Router.current().params._id).attrib, where Collection is your collection and attrib is the non-_id value you want to grab.
Also did not need tracker.autorun, that was throwing me off as well.
This is the html:
<div ng-controller="FilterController as ctrl">
<div>
All entries:
<span ng-repeat="entry in ctrl.array">{{entry.from}} </span>
</div>
<div>
Entries that contain an "a":
<span ng-repeat="entry in ctrl.filteredArray">{{entry.from}} </span>
</div>
</div>
This is my script:
angular.module('myApp', ["firebase"]).
controller('FilterController', ['filterFilter', '$firebase', function(filterFilter, $firebase) {
var ref = new Firebase("https://******.firebaseio.com/");
this.array = $firebase(ref).$asArray();
this.filteredArray = filterFilter(this.array, 'a');
}]);
The result from filteredArray is just empty. What have I done wrong? Many thanks for any insight.
The data loads in an asynchronous manner. This is JavaScript 101 and you should probably spend a little time on asynchronous ops before you try to tackle Angular and Firebase.
Your filter call happens probably even before the request is sent to the server, thus the array is still empty.
You can use the $loaded method to wait for the initial data to be downloaded. However, your filtered results will still be static, circumventing the real-time capabilities.
this.array.$loaded(function() {
console.log(this.array.length); // current length at time of loading
});
console.log(this.array.length); // 0, hasn't loaded yet
Generally, you should not be doing things like this at the code level, but instead letting Angular and Firebase work their magic; move it into the view:
<li ng-repeat="item in array | filter:searchText" />
If you find yourself trying to manipulate arrays in your controller to transform data, check out $extendFactory instead. But mostly, just leave it alone and bind it to scope, then let Angular take care of the rest.
I am learning javascript. I want to make a small chat app using angular js.
all of the data will be stored in javascript data structures - arrays, objects and be brought out to the front using angular js and styled with css.
now I have a button in index.html like this
<div class="container">
<button ng-click="createuser"></button>
</div>
in my js I have a controller where I am hanging an array called test on angular's $scope
$scope.tests = [ {'username': 'I am Joe'}];
I am looping through the tests array with ng-repeat in index.html
<li ng-repeat="test in tests">
{{test.username}}
</li>
now I have a method on the $scope object
$scope.createuser = function createuser() {
$scope.tests.push({'username': 'Mandy'})
};
that takes the array and use the push method (of the array) to push a sample object into the tests
but when I go to index.html and click, its not working because Mandy is not getting added to the loop.
Please note the method should see the $scope.tests because its all under one controller.
Forgot the ()
ng-click="createuser()"
Let me first describe my usecase.
As a user, you should be able to create a job, while creating this job, you need to specify a field of work. These fields should be loaded dynamically from my REST API.
Being quite new to EmberJS i don't really now how i should load in these fields, as i am working in the JobNewController & route.
Should i somehow load them in my route? I have been trying something like this, but i can't get it to work, also i not quite sure what i am doing anyway.
MyApp.JobsRoute = Ember.Route.extend
setupController: (controller) ->
controller.set 'serviceFields', MyApp.ServiceField.find()
Using this template
<p>Creating a new job</p>
<div class="ui input">
Select a service field
{{#each serviceFields }}
{{name}} sdf
{{/each}}
</div>
I also tried doing this in the controller like so.
MyApp.JobsNewController = Ember.ObjectController.extend
serviceFields: MyApp.ServiceField.find()
But this yields and error
Assertion failed: Your application does not have a 'Store' property defined. Attempts to call 'find' on model classes will fail. Please provide one as with 'YourAppName.Store = DS.Store.extend()'
I guess the controller code is ran before the Store is initialized.
How should i handle this?
The proper way to get models is to use the store and find that model type. (pardon my coffeescript, I used a converter to do it for me)
MyApp.JobsRoute = Ember.Route.extend(setupController: (controller, model) ->
# return does nothing here
# this._super(controller,model); would do the default
controller.set "serviceFields", []
promise = #get("store").find("serviceField")
promise.then (records) ->
controller.set "serviceFields", records
)
This is the more common approach, btw
MyApp.JobsRoute = Ember.Route.extend(model: ->
#get("store").find "serviceField"
)
I am playing with SignalR and KnockoutJS and can't seem to find a simple way to get an array from the database presented using the MVC4 framework.
I have no problem sending a single object from the server - but when I try to send an array I get stuck. Hopefully someone with more experience can spot the probably obvious mistakes I am making, and show how this should be done (JavaScript is not my strong side). The problem as far as I can understand is the mapping of the data passed from the server. Any help is much appreciated!
The SignalR Hub (orders is a simple table with Id and Name)
public class feedHub : Hub
{
private dataContext db = new dataContext();
public void GetAll()
{
var orders = db.orders.ToArray();
Clients.getData(orders);
}
}
Simple HTML code to present the orders;
<div id="Demo">
<div data-bind="foreach: orders">
<div data-bind="html: Id"></div>
<div data-bind="html: Name"></div>
</div>
</div>
JavaScript
<script type="text/javascript">
var viewModel = {
orders: ko.observableArray(orders)
};
ko.applyBindings(viewModel, $("#Demo")[0]);
$(function () {
// Client side version of the feebHub class
var hubcon = $.connection.feedHub;
// getData called from server
hubcon.getData = function (data) { viewModel.orders(data) };
// Start connection and call getAll
$.connection.hub.start(function () { hubcon.getAll(); });
});
</script>
A couple of points:
Just use ko.observableArray(), i.e. without the parameter
Put the call to ko.applyBindings inside your ready function, e.g. just before you get your hub reference
That should be enough to get it working. At least, it works me in this fiddle which I based on your code.
One further point though ... you are passing plain JSON objects to KO (i.e. inside your observable array). This is like data-binding in C# against some classes that do not implement INotifyPropertyChanged. IOW the binding will work properly once and changes in the objects will never get reflected on the UI. If you want SignalR to feed changes into your objects, then they will need to have observable properties and you might want to look into the KO mapping plugin.