Not able to access json object in Backbone View Handlebars - javascript

Backbone View:
var $ = require('jquery'),
Handlebars = require('handlebars'),
Backbone = require('backbone'),
channel = require('../../templates/dashboard/channelstats.html'),
channelstatsCollection = require('../../collections/dashboard/ChannelStatsCollection'),
mainJs = require('../../libs/main');
var ChannelStatsView = Backbone.View.extend({
el: "#divstatsview",
initialize: function () {
this.collection = new channelstatsCollection();
var api_token = mainJs.get_api_token();
this.collection.fetch(
{
headers: {'Authorization': 'Bearer ' + api_token.access_token},
success: function (collection, response, options) {
var tpl = channel;
console.log(JSON.stringify(response));
$(this.el).html(tpl({orders:JSON.stringify(response)}));
}
}
);
}
});
// Our module now returns our view
module.exports = ChannelStatsView;
Handlebars template:
{{#orders.records}}
{{#by_status}}
<div class="col-lg-2 col-md-6 new-item centered white-panel">
<h2 class="white-header"> {{PROCESSING}}</h2>
<!-- <div class="text-danger"><i class="fa fa-bell fa-3x"></i></div>-->
<div class="clearfix"></div>
<div> <b>{{PROCESSING}} new </b>Orders</div>
<div> <b>{{SHIPPED}} </b3>Pending</div>
<br/>
<div><i class="fa fa-refresh fa-2x"></i></div>
Sync
</div>
{{/by_status}}
{{/orders.records}}
My JSON:
{
"metadata":{
"recordcount":1
},
"records":[
{
"count":0,
"by_status":[
{
"OUTOFSTOCK":0
},
{
"PROCESSING":19,
"by_channel":[
{
"Some":1
},
{
"Some1":18
}
]
},
{
"RECEIVED":0
},
{
"SHIPPED":26,
"by_channel":[
{
"Demo":26
}
]
}
]
}
]
}
I get a blank page and nothing shows up.
NOTE:
I am using hbsfy to compile my templates for me so tpl is a compiled template and not an html file.
EDIT:
My json object

You need to use built-in each helper to iterate over the records and by_status arrays.
{{#each orders.records}}
{{#each by_status}}
<div class="col-lg-2 col-md-6 new-item centered white-panel">
<h2 class="white-header">{{PROCESSING}}</h2>
<div class="clearfix"></div>
<div><b>{{PROCESSING}} new </b>Orders</div>
<div><b>{{SHIPPED}} </b>Pending</div>
<br/>
<div><i class="fa fa-refresh fa-2x"></i></div>
Sync
</div>
{{/each}}
{{/each}}
I am not sure exactly what you are trying to accomplish, but the code above will output 4 divs. It will output 1 div for each element in the by_status array. It seems like you are just trying to output 1 div with a summary. If that is the case, you would probably need to do something like below.
<div class="col-lg-2 col-md-6 new-item centered white-panel">
<h2 class="white-header">{{orders.records.0.by_status.1.PROCESSING}}</h2>
<div class="clearfix"></div>
<div><b>{{orders.records.0.by_status.1.PROCESSING}} new </b>Orders</div>
<div><b>{{orders.records.0.by_status.3.SHIPPED}} </b>Pending</div>
<br/>
<div><i class="fa fa-refresh fa-2x"></i></div>
Sync
</div>
The code above assumes that the PROCESSING and SHIPPED elements always exist in the same place in the by_status array. This is probably not very wise. My recommendation would be to change the structure of your JSON response, or if that is not possible do some processing in your Backbone view before passing it to the template.
EDIT: Worked when i made my code to the following below
<div class="col-lg-2 col-md-6 new-item centered white-panel">
<h2 class="white-header">{{orders.records.0.by_status.1.PROCESSING}}</h2>
<div class="clearfix"></div>
<div><b>{{orders.0.records.0.by_status.1.PROCESSING}} new </b>Orders</div>
<div><b>{{orders.0.records.0.by_status.3.SHIPPED}} </b>Pending</div>
<br/>
<div><i class="fa fa-refresh fa-2x"></i></div>
Sync
</div>

Related

Updating module on instant change when list is updated

Sorry for the long post, but I tried explaining things in as much detail as possible.
So as I dive deeper into JavaScript and start learning more and more about AJAX requests and other components, I've stumbled across something that I can't seem to figure out.
So below, I will explain what I'm doing and what I would like to do, and see if someone has some guidance for me.
So here is my Vue.js app:
new Vue({
name: 'o365-edit-modal',
el: '#o365-modal-edit',
data: function() {
return {
list: {},
}
},
created() {
this.fetchApplicationsMenu();
},
methods: {
fetchApplicationsMenu() {
var self = this;
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'GET',
}).then(menu => self.list = menu.data);
},
changed() {
const selected = this.$data.list.selected;
function get_ids(list, field) {
const output = [];
for (let i=0; i < list.length ; ++i)
output.push(list[i][field]);
return output;
}
const result = get_ids(selected, "id");
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'PUT',
data: {
ids: result,
},
}).then((post) => {
return post;
},
(error) => {
console.log(error);
});
},
add(x) {
this.$data.list.selected.push(...this.$data.list.available.splice(x, 1));
this.changed();
},
remove(x) {
this.$data.list.available.push(...this.$data.list.selected.splice(x, 1));
this.changed();
},
},
});
Then here is the HTML portion that I'm using to render the two columns:
<div class="column is-half-desktop is-full-mobile buttons">
<nav class="level is-mobile mb-0">
<div class="level-left">
<div class="level-item is-size-5 has-text-left">Selected</div>
</div>
<div class="level-right">
<div class="level-item">
<i class="fas fa-sort-alpha-up is-clickable"></i>
</div>
</div>
</nav>
<hr class="mt-1 mb-3">
<draggable class="list-group"
v-model="list.selected"
v-bind="dragOptions"
:list="list.selected"
:move="onMove"
#change="changed">
<button class="button is-fullwidth is-flex list-group-item o365_app_handle level is-mobile" v-for="(app, index) in list.selected" :key="app.id">
<div class="level-left">
<span class="icon" aria-hidden="true">
<img :src="app.icon_url" />
</span>
<span>{{app.name}}</span>
</div>
<div class="level-right">
<span class="icon has-text-danger is-clickable" #click="remove(index)">
<i class="fas fa-times"></i>
</span>
</div>
</button>
</draggable>
</div>
<div class="column is-half-desktop is-full-mobile buttons">
<div class="is-size-5 has-text-left">Available</div>
<hr class="mt-1 mb-3">
<draggable class="list-group"
v-model="list.available"
v-bind="dragOptions"
:list="list.available"
:move="onMove">
<button class="button is-fullwidth is-flex list-group-item o365_app_handle level is-mobile" v-for="(app, index) in list.available" :key="app.id">
<div class="level-left">
<span class="icon" aria-hidden="true">
<img :src="app.icon_url" />
</span>
<span>{{app.name}}</span>
</div>
<div class="level-right">
<span class="icon has-text-primary is-clickable" #click="add(index)">
<i class="fas fa-plus"></i>
</span>
</div>
</button>
</draggable>
</div>
That outputs the following items, and all works great. See the video display below of each component working as needed. This all works great! I'm calling the changed() method on add and remove which grabs all the IDs and stores them in the DB via an endpoint.
The Problem:
Now I have the following dropdown menu, which depends on the fh/v1/menus/applications endpoint to pull in all the items as shown below:
As you can see below, when I open the dropdown, it has three apps, when I open the cog wheel and remove one of the apps and it saves it but the dropdown doesn't get automatically updated, I have to refresh the page and then I will see the updates.
Does anyone know how to fetch the new items without a refresh?
Here is the HTML and the JS for the dropdown piece:
HTML: As you can see in there, I have data-source="applications" which pulls in the items inside the init_menu as shown in the JS.
<div class="dropdown-menu" id="dropdown-o365" role="menu">
<div class="dropdown-content">
<div class="container is-fluid px-4 pb-4">
<?php if ($application = Applications::init()): ?>
<div class="columns">
<div class="dropdown-item column is-full has-text-centered is-size-6">
<div class="level is-mobile">
<div class="level-left">
<?= $application->get_name() ?>
</div>
<div class="level-right">
<a class="navbar-item modal-element icon" id="o365-apps-cogwheel" data-target="o365-modal-edit" aria-haspopup="true">
<i class="fa fa-cog"></i>
</a>
</div>
</div>
</div>
</div>
<div class="columns is-multiline" data-source="applications"></div>
<?php else: ?>
<div class="columns">
<div class="column is-full">
No applications present.
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
Then here is the JavaScript. I initilize the method inside DOMContentLoaded using init_menu('applications');:
function init_menu(paths)
{
paths.forEach(path => {
const target = document.querySelector('[data-source=' + path + ']');
if (target) {
wp.api.loadPromise.done(function () {
const Menus = wp.api.models.Post.extend({
url: wpApiSettings.root + 'fh/v1/menus/' + path,
});
const menus = new Menus();
menus.fetch().then(posts => {
// This returns the data object.
const data = posts.data;
let post_list;
// Check if it's an array and see if selected is empty otherwise show available.
if (Array.isArray(data.selected) && data.selected.length !== 0) {
post_list = data.selected;
} else {
post_list = data.available;
}
post_list.forEach(function (post) {
switch(path) {
case 'applications':
target.appendChild(create_apps_dom_tree(post));
break;
default:
console.log('Path route is invalid.');
break;
}
})
})
})
}
});
}
function create_apps_dom_tree(post) {
const {
icon_url,
url,
name,
} = post
const container = document.createElement('div');
container.className = 'column is-one-third is-flex py-0';
const anchor = document.createElement('a');
anchor.href = url;
anchor.className = 'dropdown-item px-2 is-flex is-align-items-center';
const figure = document.createElement('figure');
figure.className = 'image is-32x32 is-flex';
const img = document.createElement('img');
img.src = icon_url;
const span = document.createElement('span');
span.className = 'pl-2';
span.textContent = name;
figure.appendChild(img);
anchor.append(figure, span);
container.appendChild(anchor);
return container;
}
If anyone has some guidance or an answer on how to pull in live data from the database on the fly, that would be amazing.
Basically, I need my data-source: to automatically grab the items when my vue/db request is sent so I don't have to refresh the page.
Inside my Vue app, I have the following method:
fetchApplicationsMenu() {
var self = this;
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'GET',
}).then(menu => self.list = menu.data);
},
which calls a GET request and then stores the data inside the return { list: {} }.
A quick fix might be to just invoke init_menu() from the component's beforeDestroy() hook, called when the dialog closes. You might choose to do it from changed() instead if the dropdown is still accessible with this dialog open.
new Vue({
// option 1:
beforeDestroy() {
init_menu('applications');
},
// option 2:
methods: {
changed() {
init_menu('applications');
}
}
})
Alternative: You already know what the final application list is in changed(), so you could update the dropdown with the new list from that method.
function update_menu(path, post_list) {
const target = document.querySelector('[data-source=' + path + ']');
// remove all existing children
Array.from(target.childNodes).forEach(x => x.remove());
post_list.forEach(post => target.appendChild(create_apps_dom_tree(post)))
}
new Vue({
methods: {
changed() {
update_menu('applications', this.$data.available);
}
}
})

how to add record to array

I'm beginner in Emberjs, so i need to pass selected item in list to basket.
I have route catalog
<div class="flexbox">
{{left-menu-bar}}
<div class="main">
{{side-basket items=items}}
<div class="catalog-container">
<div class="container-inner">
{{#list-filter filter=(action 'filterByName') as |resultItems i|}}
<ul class="responsive-table">
<li class="table-header" >
<div class="col col-1">Наименование</div>
<div class="col col-2 radio-group">Год<i class="fas fa-angle-up angle angle-left radio" note="up" {{action 'sortColumn' value="target.note"}}></i><i class="fas fa-angle-down angle radio" data-value="down"></i></div>
<div class="col col-3">Количество<i class="fas fa-angle-up angle angle-left radio"></i><i class="fas fa-angle-down angle"></i></div>
<div class="col col-4">Цена<i class="fas fa-angle-up angle angle-left"></i><i class="fas fa-angle-down angle"></i></div>
<div class="col col-5">Примечание</div>
</li>
{{#each resultItems as |itemUnit|}}
{{item-list item=itemUnit gotItem=(action 'getBasketItem')}}
{{/each}}
</ul>
{{/list-filter}}
</div>
</div>
</div>
catalog.js controller
export default Controller.extend({
items: [],
actions: {
filterByName(param) {
if (param !== '') {
return this.get('store').query('item', {name: param})
}
}
getBasketItem(param){
if (param !== '') {
var item = this.get('store').query('item', {name: param});
//how to add item in items to use in side-basket
}
}
}
});
component item-list.hbs
<li class="table-row hvr-grow" {{action 'handleItem' item.name}}>
{{yield result}}
<div class="col col-1" data-label="Наименование">{{item.name}}</div>
<div class="col col-2" data-label="Год">{{item.year}}</div>
<div class="col col-3" data-label="Количество">{{item.quantity}}</div>
<div class="col col-4" data-label="Цена">{{item.cost}}</div>
<div class="col col-5" data-label="Примечание">{{item.info}}</div>
</li>
item-list.js
export default Ember.Component.extend({
selectedIndex : false,
actions: {
handleItem(param) {
let handledItem = this.get('gotItem');
handledItem(param);
}
}
});
and side-basket component with nested basket-list component
scheme
test
test with manual writing
how to realise this transfer?
In the long-run, you will find it easier if you move your data fetching to your route rather than the controller and just use query params in the controller. If you make this change, when you set your filter you can update the query params, which will in turn cause the router to refresh the model. The Ember docs explain this quite well on this page:
https://guides.emberjs.com/release/routing/query-params/
and you also need to look at this section:
https://guides.emberjs.com/release/routing/query-params/#toc_opting-into-a-full-transition
Note: this.get('store').query('item', {name: param}) in getBasketItem is returning a promise. If you change to fetching your data in the route using query params, you will be able to access your data via this.get('model') in your component and reference model in your route template - both of which will be the resolved data rather than a promise.
That should then simplify your code and make it easier to work with actual items in the controller and component. Rather than needing to fetch the item from the store in getBasketItem, you should be able to pass the item as a parameter and push it onto your items property with something like this.get('items').pushObject(selectedItem).

Unable to post ng-model data

I have an angular app that consists of a three tabbed form. Each tab is using its own controller and designated service.
When the submit button is pressed on the form, the function found in MainCtrl entitled $scope.postis suppose to post all form field data that was retrieved from each form fields ng-model.
On the server side, I receive the posted object that is formed in the Main Controller. This POST contains all the values for each object property except for work. I receive an empty object as a value for the work property.
Why am I unable to retrieve the work.comments ng-model found in the code for my template? Even when console loggin, I am unable to retrieve this item.
This is my first go at an Angular app and I am really trying to learn how to do things properly. If you happen to notice something inherently stupid, please do let me know.
Controller:
.controller('WorkCtrl', function ($scope, $ionicModal, TaskService, WorkService) {
$scope.work = {};
WorkService.add($scope.work);
});
Service:
.factory('WorkService', function() {
var work = {};
return {
add: function(data) {
work['work'] = data;
//work = data;
},
list: function() {
return work;
}
};
})
Template:
<div class="list" ng-controller="WorkCtrl">
<label class="item item-input item-floating-label">
<button class="button button-clear item-icon-right" ng-click="newTask()">New Task<i class="icon ion-ios-plus-outline"></i></button>
</label>
<label class="item item-input item-floating-label">
<span class="input-label">Comments</span>
<textarea placeholder="Comments" rows="6" ng-model="work.comments"></textarea>
</label>{{work | json}}
</div>
<div ng-controller="TaskCtrl">
<div ng-show="ifTasks()">
<div class="row header">
<div class="col tableRow">Task</div>
<div class="col tableRow">Edit</div>
<div class="col tableRow">Remove</div>
</div>
<div class="row" ng-repeat="task in tasks track by task.id">
<div class="col tableRow">{{task.choice}}</div>
<div class="col tableRow" ng-controller="WorkCtrl"><button class="button button-small button-balanced" ng-click="editTask({{task.id}})">Edit</button></div>
<div class="col tableRow"><button class="button button-small button-assertive" ng-click="del({{task.id}})">Delete</button></div>
</div>
</div>
</div>
Main Controller:
.controller('MainCtrl', function($scope, $http, SiteService, TaskService, WorkService, LabourService) {
$scope.toFetch = [];
var obj = {
site: SiteService.list(),
tasks: TaskService.list(),
work: WorkService.list(),
labour: LabourService.list()
};
$scope.post = function() {
console.log(obj);
$http({
url: 'http://localhost:1818/send/report',
method: 'POST',
data: obj,
headers: {'Content-Type': 'application/json'}
})
.then(function(data) {
console.log(data);
});
};
});

Getting null result on page using angularjs function call

call other page on finish click event :-
<a class="btn btn-default finish" style="display:none" ng-click="result()" onclick="alert('Thank you ..');">Finish</a>
My html code look like below for other page :-
<div class="tab-pane fade" id="messages" ng-init="result()">
<div class="all_ques_back col-md-12" ng-repeat="ans in correctAnswer">
<div class="col-xs-1 col-md-1"><i class="fa fa-times-circle fa-2x col_padd wrong_ans_font"></i></div>
<div class="col-xs-9 col-md-10 col_padd"><div class="all_ques" ng-repeat=" (key, val) in ans">hello your ans {{val}}</div></div>
<div class="col-xs-1 col-md-1 col_padd"><i class="fa fa-angle-right right_arrow"></i></div>
</div>
</div>
and my controller code look like below :-
$scope.correctAnswer = [];
$scope.result = function () {
$(".optionlist").find("li").each(function () {
if ($(this).attr('id') == 'true') {
var label = $(this).parent('ul').attr('que-label');
var value = $(this).find('input[type=checkbox]').attr('value');
$scope.correctAnswer.push({ "label": label }, { "Option": value });
}
});
console.log($scope.correctAnswer);
$location.path("/reviewans");
}
The function on the controller are looking for a "li" tag, but on html you define the ngRepeat on a "div" tag. The ngRepeat will repeat all the HTML code at tag it's defined on. So, you can try:
<div class="col-xs-9 col-md-10 col_padd"><ul><li class="all_ques" ng-repeat=" (key, val) in ans">hello your ans {{val}}</li></ul></div>
Like Okazari commented, don't use Jquery inside your angularJs code. I recommend to take a time to read angularJS docs. It's pretty easy to learn. And about ngRepeat, you can see here.

Angular/Rails get last saved data from table

I am new to AngularJS and I am trying to incorporate Angular into my already built Rails app. In my rails app have a the ability to post to a feed. I have Angular working so it retrieves the feed and also posts the latest record to the feed.
My issue is that it only post the contents of the form. I am looking to retrieve the user id as well so I can render out the user's name, profile pic, etc. This information shows up when I refresh the page but does not show right after the post happens, only the contents of the post shows.
microposts.js.coffee
app = angular.module('micropostApp', ['ui.router', 'ngResource'])
app.factory 'Micropost', ["$resource", ($resource) ->
return $resource('/')
]
app.factory 'createMicropost', ["$resource", ($resource) ->
return $resource('/microposts')
]
app.factory 'Comments', ["$resource", ($resource) ->
return $resource('/microposts/:id/comments', id: '#id')
]
app.controller 'MicropostsController', [
'$scope'
'Micropost'
'createMicropost'
'Comments'
($scope, Micropost, createMicropost, Comments) ->
Micropost.query (data) ->
$scope.microposts = data
$scope.addPost = ->
if !$scope.newPost or $scope.newPost == ''
return
post = createMicropost.save($scope.newPost)
console.log("POST: " + angular.toJson(post))
$scope.microposts.unshift(post)
$scope.newPost = {}
]
microposts controller
respond_to :json
def create
#micropost = current_user.microposts.create!(micropost_params)
respond_with #micropost
end
view
<div ng-app="micropostApp" ng-controller="MicropostsController">
<div class="row">
<div class="col-md-12">
<form ng-submit="addPost()" style="margin-top:30px;">
<div class="form-group">
<input type="textarea"
class="form-control"
placeholder="Post an update..."
ng-model="newPost.content"></input>
</div>
<button type="submit" class="btn btn-primary pull-right">Post</button>
</form>
</div>
</div>
<div ng-repeat="post in microposts">
<div class="row">
<div class="panel panel-default">
<div class="panel-body">
<li id="{{post.id}}">
<div class="row">
<div class="col-md-12">
<aside>
{{post.user.name}}
</aside>
<aside>
<li id="feed-fat-menu" class="dropdown pull-right feed-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<b class="glyphicon glyphicon-chevron-down"></b>
</a>
<ul class="dropdown-menu">
<li>Delete</li>
</ul>
</li>
<span class="user"> {{post.user.name}} </span>
<span class="content">{{post.content}}</span>
<hr>
<span class="timestamp pull-right"> Posted {{post.created_at}} ago. </span>
<span class="pull-left" style="padding-right: 5px;"><a>Comment </a>({{post.comments.length}})</span>
<span class="pull-left" style="padding-right: 5px;">
<a>Likes</a> ({{post.likes.length}})
</span>
<br />
</aside>
</div>
</div>
</li>
</div>
</div>
</div>
</div>
I'm not familiar with coffescript. If it's loading in the data on page reload, I would just add a call to whatever function is triggering that in the $scope.addPost call stack.
$scope.addPost = function(){
if(!$scope.newPost || $scope.newPost == ''){
return
}
post = createMicropost.save($scope.newPost);
console.log("POST: " + angular.toJson(post));
$scope.microposts.unshift(post);
$scope.newPost = {};
Micropost.get();
}
app.factory("Micropost", ["$resource", function($resource) {
return $resource("/", {}, {
get: {
method: "GET",
cache: true
}
});
}]);

Categories

Resources