AppGyver Steroids Supersonic Views - javascript

I'm trying to get my head around switching views / passing views to another view.
I have an app that is calling in a kimono API, that's all setup with the supersonic background and looks fine. I have 1 string and 2 objects in the API. I have a page that is calling in the full list of events using a page called event:
{{ event.eventdescription }}
The Event#Index controller is:
angular
.module('event')
.controller("IndexController", function ($scope, Event, supersonic) {
$scope.events = null;
$scope.showSpinner = true;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
$scope.events = events;
$scope.showSpinner = false;
});
});
});
And all of that displays properly. The issue is when I click on one of those items shown which should go to the specific event I get nothing. And I'm sure I'm doing this wrong or don't understand enough about switching views. I've read many examples, but I'm not getting how it all goes together.
here is my event#show page. Very generic just trying to load any information at this point.
<div ng-controller="ShowController">
<super-navbar>
<super-navbar-title>
Show
</super-navbar-title>
</super-navbar>
<div class="padding">
{{ event.eventdescription }}
</div>
</div>
And the showcontroller:
angular
.module('event')
.controller("ShowController", function ($scope, Event, supersonic) {
$scope.events = null;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
});
});
});
And this always returns a blank page. When i check the log it says Undefined.undefined which i'm not sure what that means.
Any insight on this is greatly appreciated. In the appgyver docs I saw something called.
var view = new supersonic.ui.View("bananas#show");
supersonic.ui.layers.push(view);
But I'm not sure how to use this?
ANY insight is appreciated.
So, UPDATED I have:
here's the event#index i'm working with.
<div ng-controller="IndexController">
<super-navbar>
<super-navbar-title>
Event Index
</super-navbar-title>
</super-navbar>
<ul class="list" ng-hide="events.length == 0">
<super-navigate view-id="event#show" data-params-id="{{event.id}}" ng-repeat="event in events">
<li class="item item-icon-right">
<h2 ng-bind="event.EventTitles['text']"></h2>
<img ng-src="{{ event.HeadlineImages.src }}" width="100px" height="100px">
<p> {{ event.eventdescription }} </p>
<i class="icon super-ios7-arrow-right"></i>
</li>
</super-navigate>
</ul>
</div>
And the Index Controller
angular
.module('event')
.controller("IndexController", function ($scope, Event, supersonic) {
$scope.events = null;
Event.all().whenChanged( function (events) {
$scope.$apply( function () {
$scope.events = events;
});
});
});
The show html page.
<div ng-controller="ShowController">
<super-navbar>
<super-navbar-title>
Show
</super-navbar-title>
</super-navbar>
<div class="padding">
<p>
{{event.eventdescription}}
</p>
</div>
</div>
The ShowController
angular
.module('event')
.controller("ShowController", function ($scope, Event, supersonic) {
supersonic.ui.views.current.params.onValue( function (Event) {
$scope.events = event.id;
});
Event.find($scope.events).then( function (Event) {
$scope.$apply( function () {
$scope.event = Event;
});
});
});
And I also updated the structure.coffee as so
rootView:
location: "event#index"
preloads: [
{
id: "event#show"
}
{
id: "using-the-scanner"
location: "example#using-the-scanner"
}
]
Any help is appreciated.

It doesn't look like the data is being set in the your ShowController. I commented about this before. I think you need to pass the id of the event using <super-navigate> with a location property and a data-params-id or whatever you want the parameter name to be. Then in your ShowController you can access it with:
supersonic.ui.views.current.params.onValue( function (values) {
// values.nameOfPropertyPassedInCouldBeEventId
$scope.id = values.id;
});
Then you might be able to do something like this to access the Event by id:
Event.find($scope.id).then( function (theEvent) {
$scope.$apply( function () {
$scope.event = theEvent;
});
});
Now in your view where you have {{ event.eventdescription }} there should be some data.
And another piece for when the view is visible meaning every time you see that view page this will fire:
supersonic.ui.views.current.whenVisible( function () {
// your code for watching events
});

Ok, after a couple weeks of trying to get this working and although, I still haven't been able to get this to work yet.. I think I'm getting somewhere with this FINALLY... It seems the biggest problem here is using Kimono and AppGyver. The JSON file has been updated in Kimono using:
function transform(data) {
data.results.collection1 = data.results.collection1.map(function(o) {
o.eventdescription = {
text: o.eventdescription
}
return o;
});
return data;
}
This cleans up the JSON file exported/ coming in as API to App Gyver so that all parts are objects. ( I know, maybe not a big deal, but I just wanted to make this as clean as possible). To give you an idea of the before and after of using this script in the Kimono Modify Results box -->
BEFORE:
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "Lorem Ipsum"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "Lorem Ipsum"
},
"eventdescription":"Lorem Ipsum"
},
which leaves eventdescription as a string rather than object and then the AFTER:
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"eventdescription": {
"text": "TEXT"
},
So, after running this into Kimono as you can see all entries are "objects". And you'd use &kimmodify=1 AFTER the apikey in the link thusly:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimmodify=1
NEXT, as I was explained to by the AppGyver community one would pretty much need an "id" of sorts for each item in the JSON / API that's being created to be able to use the ShowController to create a reasonable/ feasible url string on the show.html.
Which should create something like /app/tier/showid=123456789 when going from the index to a specific entry view.
(You find the URLs by using the debug mode in AppGyver either via Safari Web Inspector on Mac with the IOS Emulator. or a browser using http://localhost:[some port number]/location/of/app when using the Android Emulator (the recommended Genymotion).
So, to do this, in Kimono use the API Hash addition &kimhash=1 to the end of your url AFTER the APIKEY but BEFORE the modify such as this:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimhash=1&kimmodify=1
. See: Kimono API Docs- Re:Hash.
This creates something like
"EventTitles": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"HeadlineImages": {
"href": "http://",
"src": "http://.jpg",
"text": "TEXT"
},
"eventdescription": {
"text": "TEXT"
},
"hash":"1a2b3c4d5e6f7g8h9z"},
a random 'indentifier' is created for each entry.
Now, that's where I'm stuck now. ...because the API URL needing to come in is:
https://www.kimonolabs.com/api/{indentifier}{apikey}&kimhash=1&kimmodify=1
and when you go to configure your API on the backend there is no area I see to enter this new &kimhash=1&kimmodify=1 that needs to be at the end of the URL to call in the correctly formatted and id'd API and as far as I can see there is no reference for doing this.
http://docs.appgyver.com/supersonic/guides/data/other-data-providers/kimono-labs/
I feel like this is the next to last step in figuring this all out and finally being able to get this up and working. The last being to obviously revisit pulling in the id to the ShowController which I'm feeling somewhat confident about if I can somehow figure out this last part.
Any ideas??

Related

SVG with Angular directive : bad performances

Its the first time I'm asking something here but i'm totally stuck.
I'm creating an interactive svg map with Angular. It works fine with D3 but I would like to use Angular, with its directives and templates.
Currently, I managed to have the map show up on screen and manipulate it, but the performances are awful.
I know that I must filter my datas but i don't know where : is it on the controller, or the directive's link, or the API ? I don't know.
Note that I'm still a noob with Angular (hardly a month), even with code since I started to learn it 3 months ago. So, you will certainly read some silly stuff here.
Anyway, my question is : what I am supposed to do to replace my complex ng-hide by a simple ng-if (i want to remove path depending on the year input).
So, I'm retrieving a JSON list of path from my API.
myjson :
[ { "land_name" : "landa", "path" : "...", "born" : 5000, end : 5152}
{ "land_name" : "landi", "path" : "...", "born" : 1200, end : 7100}
{ "land_name" : "lando", "path" : "...", "born" : 100, end : 4000} ]
my api (node + mongoose):
// get all the lands (accessed at GET http://localhost:8080/api/landapi)
.get(function(req, res) {
landapi.find(function(err, docs) {
if (err)
res.send(err);
res.json(docs);
});
});
angular :
my factory :
.factory('landDataFactory', ['$http', function($http) {
return $http.get('http://localhost:3000/api/landapi')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
my controller :
.controller('testCtrl', function($scope, landDataFactory) {
var vm = this;
vm.paysList = [];
vm.year = ''; (==> ng-model in the template)
landDataFactory.success(function(data) {
vm.land = data;
for (var i = 0; i < vm.land.length; i++) {
vm.LandList.push(vm.land[i]);
}
});
my directive :
.directive('dynamicMap', function() {
return {
restrict: 'E',
replace : true,
scope: {
datapath : '=',
year : '='
},
templateUrl : './modules/dynamicmap.tpl.html',
link : function(scope, element, attrs) {
}
};
my template :
<div>
<div class="year">
<input ng-model="year" type="number" step="50" class="inputDate col-xs-2 form-control" placeholder="date""> <br/>
</div>
<svg class="Map">
<g class="drawLand" ng-repeat="data in datapath">
<path ng-hide="year == NULL || data.born > year || data.end < year" ng-attr-d="{{ data.path }}" id="{{ data.id_land }}"></path>
</g>
</svg>
</div>
my directive when i'm calling it :
<dynamic-map datapath="vm.paysList" year="vm.year"></dynamic-map>
Thanks in advance
I am barely qualified to comment since I only played with Angular for about 2 days, but in that brief period, I found that it is extremely easy to run into performance issues if you have even medium sized data sets. From what I understand, you should try very hard to limit how many two way data bindings you create. Since your code has an ng-repeat, you should, if possible, look at making the bindings inside of the repeat one way by replacing {data:path} with {::data.path}.
My real advise though is to not use angular. From what I can tell, Angular 1.0 is a dead end since it will soon be eclipsed with Angular 2.0. In addition the entire philosophy of Angular seems built around the idea of "Look how nice this syntax is! HTML and javascript have never been easier!", to "Oh, you want to make a real app? Well here's some ugly hacks to make it perform, and by the way, don't you understand the difference between a directive and a factory-directive and a directive-factory? And how could you not know that every tag has different scoping rules...".
In short, wait for Anuglar 2.0 or look for something else. I went with react.js and so far find it to lighter weight and more intuitive and a better for long run productivity.

retrieve image url from object values - angular js

Very noob here to Angular JS.
I'm working on a project and I have a solution to this, but I was wondering if theres a better approach
thats a bit more "angular-esque" if you will.
Essentially for each videoid, it performs an ajax request locating that videoid's image
and places that into the src attribute.
In pure javascript this would be no problem, and I've figured out one solution but it sort of deviates away from the angular approach.
I was wondering if it's possible to do something sort of what I have here. (obviously this doesnt work yet)
Markup
<ul class="storysContainer">
<li video-on-click ng-repeat="video in videos" id="video{{$index}}" >
<img ng-src="{{getVideoThumb()}}">
</li>
</ul>
JS
$scope.videos = [
{
'videoid': '1122345'
},
{
'videoid': '1134567'
},
{
'videoid': '2234456'
}
];
$scope.getVideoThumb = function() {
$.get("http://blahblah.com/id=" + url,function(data){
var urlMain = data.channel.item["media-group"]["media-thumbnail"]["#attributes"].url;
array.push(urlMain);
});
return array;
}
Thanks
edit:
This is the solution that i came up with..not sure if it's necessarily the best angular-esque appraoch, but it works.
angular.forEach($scope.videos, function(data) {
var dataid = "http://blablah.com?id=" + data.videoid;
console.log(dataid);
$.get(dataid, function(img){
var urlMain = img.channel.item["media-group"]["media-thumbnail"]["#attributes"].url;
$('#thumb' + data.videoid).attr('src', urlMain);
//array.push(urlMain);
});
});
I would declare the URL for each object within the videos object array. That way you can just bind the value in your presentation layer.
So maybe something like
$scope.videos = [
{
'videoid': '1122345'
},
{
'videoid': '1134567'
},
{
'videoid': '2234456'
}
];
//modified this a little. I take it this call accepts the videoID as a parameter
//and returns the url for a single video?
//I wasn't sure what the 'array' variable you were using was.
//I am also using angular $http service to make this ajax call
$scope.getVideoThumb = function(videoID) {
$http.get("http://blahblah.com/id=" + videoID).success(function(data){
var urlMain = data.channel.item["media-group"]["media-thumbnail"]["#attributes"].url;
return urlMain;
});
}
//iterate through each object and assign it a 'URL' property to the result of 'getVideoThumb(videoID)'
for(var i = 0; i < $scope.videos.length; i++){
$scope.videos[i].URL = $scope.getVideoThumb($scope.videos[i].videoid);
}
and now in our presentation layer we can just bind the value of URL to the ng-src
<li video-on-click ng-repeat="video in videos" id="video{{$index}}" >
<img ng-src="{{video.URL}}">
</li>

Backbone: Collection not rendered in View even though it's fetched

I know that there are a few questions around regarding this, but the answers are not very clear for me to implement. That's why I'm asking this question again so I can have a clear and simple answer.
I've always had trouble with Collection in Backbone, especially populating it with JSON data.
I can't seem to get the collection to render in the View, even though in firebug I can see that, it's being fetched from the server, but the screen is still empty.
Also, when I do a console.log('callers: ', this.callerList), it returns an object with models=[0]. But when I expand the object, models is full of data from the JSON file. What's going on with Backbone and it's confusing results?
Can someone please explain to me how to do it? I've been battling this for ages and I can't get my head around it.
Many Thanks
JS:
(function($, window) {
// model
var CallerModel = Backbone.Model.extend({});
// collection
var CallersList = Backbone.Collection.extend({
model: CallerModel,
url: 'js/json/callers.json'
});
// view
var CallerView = Backbone.View.extend({
el: '.caller-app',
template: _.template($('#callers-template').html()),
initialize: function() {
this.callerList = new CallersList();
this.callerList.fetch();
this.callerList.bind('reset', this.render);
console.log('caller: ', this.callerList);
},
render: function(e) {
console.log('RENDER');
_.each(this.collection.models, function(caller) {
this.$el.append(this.template(caller.toJSON()));
console.log('callerList: ', caller);
}, this);
return this;
}
});
// start
var callerView = new CallerView();
}(jQuery, window));
HTML:
<!-- wrapper -->
<div class="wrapper">
<h1>Missed Calls App</h1>
<div class="caller-app"></div>
</div>
<!-- wrapper -->
<!-- templates -->
<script type="text/template" id="callers-template">
<div class="caller">
<h2><%= title %> <%= name %> called</h2>
<h3>From <%= agency %></h3>
<p>When: <%= when %></p>
<p>Contact: <%= tel %></p>
<p>Says:"<%= message %>"</p>
</div>
</script>
<!-- templates -->
JSON:
[
{
"id": 1,
"title": "Mrs",
"name": "Mui",
"agency": "Ryuzanpaku Dojo",
"when": "evening",
"tel": "0207 123 45 67",
"message": "Check your availability"
},
{
"id": 2,
"title": "Mrs",
"name": "Shigure",
"agency": "Ryuzanpaku Dojo",
"when": "evening",
"tel": "0207 123 45 67",
"message": "Check your availability"
}
]
You haven't actaully assigned a collection to your CallerView, in addition when you iterate though the collection you should be using this.collection.models instead of this.model.models
For example when initializing you caller list
initialize: function() {
initialize: function() {
this.collection = new CallersList();
this.collection.fetch();
this.collection.bind('reset', this.render);
}
And when rendering
render: function(e) {
_.each(this.collection.models, function(caller) {
this.$el.append(this.template(caller.toJSON()));
}, this);
return this;
}
Here's a link to a jsbin
Some additional points
In general you want to decouple your code as much as possible. To this end it is probably better to declare and initialize your collection outside of your view and then pass it in. This also has the advantage of making your code more reusable, for example say you wanted to render a second list of calls (let say recent calls), you can now just create a second instance of your view passing in a collection and element.
For example
var missedCalls = new CallersList();
var callerView = new CallerView({collection : missedCalls, el: '#missedCalls' });
missedCalls.fetch();
var recentCalls = new CallerList(); //you probably want to use a different url
var recentCallersView = new CallerView({collection : recentCalls, el:'#recentCalls'});
recentCalls.fetch();
Another point worth mentioning, currently you are rendering all items in your collection for each fetch, including any that have been already rendered. You might want either empty your el before rendering or listen to the add event instead and render each item individually as it's added. In addition it's worth pointing out that fetch isn't really meant to be used to load data on page load, from the documentation
Note that fetch should not be used to populate collections on page
load — all models needed at load time should already be bootstrapped
in to place. fetch is intended for lazily-loading models for
interfaces that are not needed immediately: for example, documents
with collections of notes that may be toggled open and closed.

jaydata "automatic attach of related entities" not working?

According to http://jaydata.org/blog/release-notes, below "JayData 1.3.1 Interoperability Edition", there is an item titled "Auto-attach of included child objects"
In my code, i tried this:
smarterpjs.localdb.Clientes
.include('CondicionVenta')
.include('ListaPrecios')
.include('Vendedor')
.include('Localidad')
.single(function(c) {
return c.Oid === this.ID
}, {
ID: e.model.Oid
}, function(c) {
var item = smarterpjs.crearOrdenVenta();
smarterpjs.localdb.Clientes.attach(c);
item.Cliente(c);
...
});
Problem is that c.CondicionVenta has no context set after the call to attach.
If i try latter to save that "item", it tries to save a new "CondicionVenta", a new "ListaPrecios", a new "Vendedor", a new "Localidad" too, which are wrong.
So, my question is: is that Auto-attach of included child objects" working or not? I'm using v1.3.2

JS tree Loading time issue

I have implemented the Tree with js tree and here i'm getting lot of time(Around 1 min) when loading the tree..
I wanna to find the way to reduce the time and i have over 5000 nodes in my implementaion.
In my view
$("#tree").jstree({
checkbox: {
real_checkboxes: true,
real_checkboxes_names: function (n) { return [("check_" + (n[0].id || Math.ceil(Math.random() * 10000))), n[0].id] }
}, "plugins": ["themes", "html_data", "checkbox", "sort", "ui"]
}).bind('check_node.jstree', function (event, data) {
$('#SearchView').show();
}).delegate("a", "click",
function (event, data) {
event.preventDefault();
});
html for load js tree
<tbody>
<div id="tree">
<ul>
#HtmlHelpers.RenderjsTree(Model.CurrentNode)
</ul>
</div>
</tbody>
RenderjsTree will recursively call and load the tree nodes.. any way to reduce the time?
There are a couple of ways to approach this slow loading problem.
One way is to use the ajax method in the json_data plugin of jstree. Mike Tyka has given a pretty neat description of doing that here - http://www.miketyka.com/2012/10/lazy-loading-with-jstree-and-ajax/
Another way is via a simple javascript method - if you are open to using the v3 of jstree which is still in beta version. In my project I had around 2200 nodes and the json data came from the server side via a single ajax call in less than a second. But the json parsing took around 8-10 seconds till which the page just stopped responding. Jstree v3 has a method of getting the data of a node from a function when the node is opened. I used that method and the page now loads in just under 2 seconds.
function initiate_jstree() {
$("#tree_container").jstree({
"core": {
"data": getTree,
"themes":{
"icons":false
},
},
"plugins": [ "themes", "ui" ,"types"]
});
}
function makeTreeData(node){
if(node.original && node.original.actualData){
data=node.original.actualData;
}else{
data=gData;
}
treeData=[];
for(i=0;i<data.length;i++){
iter=data[i];
item={"text": iter.data};
if(iter.children){
item["children"]=true;
item["actualData"]=iter.children;
}
treeData.push(item);
}
return treeData;
}
var getTree = function (obj, cb) {
console.log("called");
data=makeTreeData(obj);
cb.call(this,
data);
}
initiate_jstree();
The gdata variable here is a global variable in which the data to be loaded is stored in json format.
Here is the code on jsfiddle - http://jsfiddle.net/Lge8C/

Categories

Resources