Dojo Grid reload data file upon button click - json

I know there maybe similar questions out there, but I still cannot find the answer. Much appropriate anyone who can help me.
There are 5 departments, and each department has 4 products. So I created 5 buttons and 4 tabs, each tab contains a grid. By default the department A is loaded, user can switch tabs to see different products information from this department. By click another button B, department B's information will loaded to all 4 tabs.
Click each button will send a ajax request to the back end PHP code, PHP will read XML file do calculation and write data to "data\productA.json", "data\productB.json" , "data\productC.json" , "data\productD.json" files, respect to product A to product D for that specific department. Note that the first tab always read from "data\product A" file, no matter which button you clicked, same for other tabs.
Then the JavaScript will read from the "data\product?.json" file and present data in the grid.
When the page loads, first department's information is correctly loaded into the grid. However, if I change to another department (click button), the grid won't reload data from the json files.
Here is JS part:
dojo.addOnLoad(function() {
//init the first main column when load the page.
getDepartmentA();
var layout = [[
new dojox.grid.cells.RowIndex({ width: 5 }),
{name: 'Name', field: 'name'},
{name: 'Count', field: 'count'},
{name: 'Percent', field: 'percent'}
]];
var store = new dojo.data.ItemFileReadStore( { url: "data/productA.json" } );
var grid = new dojox.grid.DataGrid( { store: store, rowsPerPage: 200, style: "height:600px; width:874px;", structure: layout},
dojo.byId("grid1"));
grid.startup();
dojo.connect( dijit.byId("column3"),"onShow", dojo.partial( createGrid, "3") );
dojo.connect( dijit.byId("column4"),"onShow", dojo.partial( createGrid, "4") );
dojo.connect( dijit.byId("column5"),"onShow", dojo.partial( createGrid, "5") );
});
function getDepartmentA() {
dojo.xhrGet( {
url: "department_A_process.php",
handleAs: "json",
load: function(response) {
var tempgrid = grids[0];
var tempresponse = eval("("+response+")");
var tempstore = new dojo.data.ItemFileReadStore({url: "data/productA.json" }); //updated store!
var tempModel = new dojox.grid.data.DojoData(null, tempstore, {query:{productName:'*'}, clientSort: true});
tempgrid.setaModel(tempModel);
tempgrid.refresh();
console.dir(response); // Dump it to the console
}
});
}
function createGrid( id ) {
console.log("Calling createGrid function now!");
var layout = [[
new dojox.grid.cells.RowIndex({ width: 5 }),
{name: 'Name', field: 'name'},
{name: 'Count', field: 'count'},
{name: 'Percent', field: 'percent'}
]];
if (! grids[id] ) {
if (id =="1"){
var store = new dojo.data.ItemFileReadStore( { url: "data/productA.json" } );
console.log( "I am in tab1");
} else if (id =="3"){
var store = new dojo.data.ItemFileReadStore( { url: "data/productB.json" } );
console.log( "I am in tab3");
} else if (id =="4"){
var store = new dojo.data.ItemFileReadStore( { url: "data/productC.json" } );
console.log( "I am in tab4");
} else if (id =="5"){
var store = new dojo.data.ItemFileReadStore( { url: "data/productD.json" } );
console.log( "I am in tab5");
}
var grid = new dojox.grid.DataGrid( { store: store, rowsPerPage: 200, style: "height:600px; width:874px;", structure: layout},
dojo.byId("grid" + id ));
grid.startup();
grids[id] = grid;
console.log( grid );
}
}
My index page is like:
<div id="mainTabContainer" dojoType="dijit.layout.TabContainer" doLayout="false">
<div id="column1" dojoType="dijit.layout.ContentPane" title="Label by Brand" selected="true">
<h1>Label by Brand</h1>
<div class="partsContainer">
<div id="grid1" class="gridContainer">
</div>
</div>
</div>
<div id="column3" dojoType="dijit.layout.ContentPane" title="Session Types">
<h1>Session Types</h1>
<div class="partsContainer">
<div id="grid3" class="gridContainer">
</div>
</div>
</div>
<div id="column4" dojoType="dijit.layout.ContentPane" title="Labels by Session">
<h1>Labels by Session</h1>
<div class="partsContainer">
<div id="grid4" class="gridContainer">
</div>
</div>
</div>
<div id="column5" dojoType="dijit.layout.ContentPane" title="Monthly Report">
<h1>Monthly Report</h1>
<div class="partsContainer">
<div id="grid5" class="gridContainer">
</div>
</div>
</div>
</div>
The JSON file looks like:
{
identifier: "productName",
label: "productName",
items: [
{ "productName" : "p1", "count" : 3362, "percent" : "32.8" },
{ "productName" : "p2", "count" : 421, "percent" : "4.1" },
{ "productName" : "p3", "count" : 526, "percent" : "5.1" },
{ "productName" : "p4", "count" : 1369, "percent" : "13.4" },
...
{ "productName" : "Total", "count" : 10242, "percent" : "100" }
]
}
Anyone can help out, how to reload the file that generated by PHP to the grid? Thank you.

I don't see any code involving a button or requesting new data for a store in your code...
To fix your issue, try adding clearOnClose:true to your store initializations. You may also need urlPreventCache:true. Firebug or any sort of net monitor will tell you if this is needed.
When the button is pressed, get the reference to the store for each grid and call store.close() then store.fetch(). This should accomplish what you are looking for by refreshing the data in the store. After this it may be necessary to call grid.render() or something similar.
One thing I should note here just to save you a possible headache later: Unless you have some sort of user hash for the directory structure and security measures in place, the way your PHP behaves by creating a single set of files for each department is likely going to result in problems with multi-user support and security issues where you can read another person's JSON responses.
Found the info here : http://livedocs.dojotoolkit.org/dojo/data/ItemFileReadStore. Search for clearOnClose for the approximate area to look for information.

Related

How to manually focus input

I want to replicate a common item list renaming feature, where you have a list of layers and if you double click a layer, it changes the layer item to an input and that input is automatically focused with its text selected as well.
In my example, I am not able to focus() the DOM element by its ref because it says it is not defined. It only works if I click a second time on the element once its changed to an input. How do I set this autofocus?
<div v-for="(item, i) in items">
<div #click="changeToInput(i)" v-if="!item.input">{{item.name}}</div>
<input ref="input" v-model="item.name" onfocus="select()" v-else>
</div>
changeToInput(i) {
this.items[i].input = true;
//this.$refs.input.focus()
}
Here is the complete example : https://codesandbox.io/s/reverent-khayyam-2x8mp?file=/src/App.vue:481-573
Two solutions:
First one: uses v-if + this.$nextTick:
v-if will insert/destroy the component when the binding expression is true/false, so in current cycle, input hasn't been in Dom tree. You have to use nextTick to wait for next cycle to get the Dom element of Input. And this.$refs.input will be one array based on how many v-if=true, so you have to filter out the this.items to find out correct index (that is why I used one combination of Array.slice and Array.filter).
Updated: The order of the elements of this.$refs.input1 is the order VNode is created. For example: clicks input2 -> input3 -> input1, the order of this.$refs.input1 is [2, 3, 1], not [1, 2, 3].
Second one: uses v-show + this.$nextTick:
It will make things easier, because v-show only update the css styles for Dom elements, it will not add/remove component instance (Vnode) from VNode tree. So the this.$refs.input will always equal this.items.length.
new Vue ({
el:'#app',
data() {
return {
items1: [
{ name: "Joe", input: false },
{ name: "Sarah", input: false },
{ name: "Jeff", input: false }
],
items2: [
{ name: "Joe", input: false },
{ name: "Sarah", input: false },
{ name: "Jeff", input: false }
],
refSort: {}
};
},
methods: {
changeToInput1(i) {
this.items1[i].input = true;
let refCount = (this.$refs.input1 && this.$refs.input1.length) || 0
refCount < this.items1.length && (this.refSort[i] = refCount)
this.$nextTick(() => {
// the purpose uses this.refSort is record the order of this.$refs.input (Its order is same as the creating order of Ref), you can uncomment below line to see the root cause
//console.log(this.$refs.input1[0] && this.$refs.input1[0].value, this.$refs.input1[1] && this.$refs.input1[1].value, this.$refs.input1[2] && this.$refs.input1[2].value)
this.$refs.input1[this.refSort[i]].focus()
})
},
changeToInput2(i) {
this.items2[i].input = true;
this.$nextTick(() => this.$refs.input2[i].focus())
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<h3>Uses v-if: <p>{{items1}}</p></h3>
<div v-for="(item, i) in items1">
<div #click="changeToInput1(i)" v-if="!item.input">{{item.name}}</div>
<input ref="input1" v-model="item.name" onfocus="select()" v-else>
</div>
<h3>Uses v-show: <p>{{items2}}</p></h3>
<div v-for="(item, i) in items2">
<div #click="changeToInput2(i)" v-show="!item.input">{{item.name}}</div>
<input ref="input2" v-model="item.name" onfocus="select()" v-show="item.input">
</div>
</div>

How can i access to 'download_url' in json using vuejs

i want to get 'tags' and 'download url' inside meta, i cannot access to 'download_url' in json how i can do it, as a backend i am using wagtail cms, and is it good to use vue js with wagtail cms (headless cms)
<template>
<div>
<div>
<b-card-group deck v-for="item in results" :key="item.id">
<b-card
>
<b-card-text>
<div v-for="block in item.body" :key="block.id">
<div v-if="block.type == 'heading'">
<h2>{{block.value}}</h2>
</div>
<div v-if="block.type == 'image'">
<img :src="'http://127.0.0.1:8000/api/v2/images/' + block.value">
</div>
<div v-if="block.type == 'paragraph'">
<h2 v-html="block.value">{{block.value}}</h2>
</div>
</div>
</b-card-text>
>
</b-card>
</b-card-group>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Home',
data () {
return {
results: null
}
},
mounted () {
axios.get('http://127.0.0.1:8000/api/v2/pages/?type=news.NewsPage&fields=intro,body,image_thumbnail')
.then((response) => (
this.results = response.data.items
))
}
}
</script>
here is json api. i accessed till id of image and do not know what to do next
{
"id": 3,
"meta": {
"type": "wagtailimages.Image",
"detail_url": "http://localhost/api/v2/images/3/",
"tags": [
"gadget",
"phone",
"samsung"
],
"download_url": "/media/original_images/affordable_new_9-7-inch_ipad_group_fan2_1_1.png"
},
"title": "affordable_new_9-7-inch_ipad_group_fan2 1 (1).png",
"width": 528,
"height": 357
}
Assuming that response.data.items is the json you showed above, you can extract just what you want from there. Right now, your component has the whole response.data.items in this.response. So if you want all of it there, keep that.
You can store the 'tags' and 'download url' to your component by adding two new pieces of data ie add them to your data:
data () {
return {
results: null,
tags: null,
downloadUrl: null
}
... and then set those in your response block from the request's response like this.
mounted () {
axios.get('http://127.0.0.1:8000/api/v2/pages/?type=news.NewsPage&fields=intro,body,image_thumbnail')
.then((response) => (
this.results = response.data.items
this.tags = response.data.items.meta.tags
this.downloadUrl = response.data.items.download_url
))
}
When dealing with nested response objects like this, it can help to make a local variable with the data and add it that way. Also, if you dont need the whole response, you can avoid storing it and just store the data from the response that you want to your component here. If you wanted to get something from your this.response, you're going to have to go deep into it. It would be cleaner to just pull out only what you need, and then use it in your code with just one {{myStuffIPulledOut}} vs {{response.thingIWantBefore.actualThing.}}

Show MongoDB info in HTML

I'm working on a website using a MEAN stack, and now I am trying to show some MongoDB data in my HTML pages by using Angular. But I don't seem to get it done.
This is the data in MongoDB I want to show in my HTML
{
"badkamer" : {
"block1" : {
"title" : "Badkamer",
"content" : "string"
}
}
}
This is the Angular function retrieving the data:
app.controller('cityCtrl', function($scope,$http){
$scope.specials = function(){
$scope.special = [];
$http.get('/specialdata').then(function(d){
$scope.special = d.data;
console.log(d.data);
},function(err){
console.log(err);
});
};
});
This is where I want it to show in my HTML:
<div ng-controller="cityCtrl" ng-init="specials()" ng-bind="special">
<div class="title">{{special.badkamer.block1.title}}</div>
<p>{{special.badkamer.block1.content}}</p>
</div>
</div>
When i console.log(d.data), I get this:
[Object]
0: Object
badkamer: Object
block1: Object
content: "Text",
title: "Badkamer"
But when I try it like this, the bind option shows all the data at once in my HTML. How can I get it working by using the Angular {{}} tags?
From the console.log, you can see that its an array, so you will need to use index, like this,
<div ng-controller="cityCtrl" ng-init="specials()" ng-bind="special">
<div class="title">{{special[0].badkamer.block1.title}}</div>
<p>{{special[0].badkamer.block1.content}}</p>
</div>
</div>
or change the code in controller.,
$scope.special = d.data[0];

How to implement nested data form in angular2

Here is Json schema :
{
"_id" : ObjectId("59031d77fd5e1c0b3c005d15"),
"resume_data" : {
"work_experience" : [
{
"company" : "example",
"website" : "example.com",
"position" : "Internship",
"highlights" : "Learn To Create API In Laravel Framework. and also Learn Angular 2 for Front end Development.",
"project_experience" : [
{
"projectName" : "Fb Project",
"teamMember" : "5",
"technology" : "PHP,Laravel-5,Angular-2,MongoDb",
"projectPosition" : "Back-end Developer"
}
]
}
]
}
}
Here is image:
I have reference of this answer but i don't know about nested form data. can anyone explain how to implement it.
Here is your code, which sets the data you are receiving from backend, here I have stored it in a variable data.
Please notice, this is a shortened version of your form, but the basics are there, you only need to add the few missing properties in corresponding form arrays.
The build of the empty form looks is just a FormArray named work_experience matching your json structure:
this.myForm = this.fb.group({
work_experience: this.fb.array([])
})
We add the fields when you are receiving the data, call a function called setWorkExperience in the callback when receiving data:
setWorkExperience(){
// get the formarray
let control = <FormArray>this.myForm.controls.work_experience;
// iterate the array 'work_experience' from your JSON and push new formgroup with properties and the inner form array
this.data.work_experience.forEach(x => {
// add the rest of your properties also below
control.push(this.fb.group({company: x.company, project_experience: this.setFormArray(x)}))
})
}
setFormArray is called from the previous function, where we patch the data with from project_experience to the inner form array:
setFormArray(x) {
// create local array which is returned with all the values from the 'project_experience' from your JSON
let arr = new FormArray([])
x.project_experience.map(y => {
// add the rest of your properties below
arr.push(this.fb.group({projectName: y.projectName}))
})
return arr;
}
The template would then look like this:
<form [formGroup]="myForm">
<!-- Outmost array iterated -->
<div formArrayName="work_experience">
<div *ngFor="let a of myForm.get('work_experience').controls; let i=index">
<h3>COMPANY {{i+1}}: </h3>
<div formGroupName="{{i}}">
<label>Company Name: </label>
<input formControlName="company" /><span><button (click)="deleteCompany(i)">Delete Company</button></span><br><br>
<!-- inner formarray iterated -->
<div formArrayName="project_experience">
<div *ngFor="let b of myForm.controls.work_experience.controls[i].controls.project_experience.controls; let j=index">
<h4>PROJECT {{j+1}}</h4>
<div formGroupName="{{j}}">
<label>Project Name:</label>
<input formControlName="projectName" /><span><button (click)="deleteProject(a.controls.project_experience, j)">Delete Project</button></span>
</div>
</div>
<button (click)="addNewProject(a.controls.project_experience)">Add new Project</button>
</div>
</div>
</div>
</div>
</form>
In the template you can see the buttons for add and delete of projects and companies. Adding and deleting companies are straightforward, where initCompany() returns a formGroup:
deleteCompany(index) {
let control = <FormArray>this.myForm.controls.work_experience;
control.removeAt(index)
}
addNewCompany() {
let control = <FormArray>this.myForm.controls.work_experience;
control.push(this.initCompany())
}
In the add project we pass as parameter from the template the current formArray control, to which we just push a new FormGroup:
addNewProject(control) {
control.push(this.initProject())
}
In the delete function we pass the current formarray as well as the index of the project we want to delete:
deleteProject(control, index) {
control.removeAt(index)
}
That should cover pretty much everything.
Plunker
Please Check it Out This
Plunker Here
Json Store Like This
{
"name": "",
"work_experience": [
{
"name": "asdasd",
"project_experience": [
{
"Project_Name": "asdasdasd"
},
{
"Project_Name": "asdasdasd"
}
]
}
]
}

How to control ng-repeat divs from ng-repeat inputs

So, just getting started in Angular and it's pretty tricky, coming from a pretty simple JS and jQuery background. Here's what I'm trying to do. I have a "tag template" that has a couple categories and then some sub-tags contained within. I have defined these as an object, with the idea that the object/file can be called via file request and manipulated, etc.
I have loaded labels and tag category inputs dynamically by using a factory service and a controller with ng-repeat. Likewise, I have deposited the subtags into another div on page2 (using jQuery mobile page swiping). I'd like to use the checkbox state of the category tags to show/hide the sub-tags on page2.
I have tried dozens of things and searched all over stackexchange, the net, etc, but is simple and straightforward and similar enough for me to get it working. If someone can point me in the right direction, that would be great. Keep in mind that my next step is to add a button on page 1 to add a new category, and buttons on page 2 to add sub-tags to the sub-tag categories.
Finally, I have one more weird thing to report. If I only have two pages in my DOM, I have some weird behavior when loading the page. If I load from page 1, the tag checkboxes do not function, and I see a slight fattening of the border of the labels. If I swipe left to page 2 and reload from this page, the borders of the labels are thin and the checkboxes function. Cannot track down why this would be happening. My hacky workaround is to add an empty page zero and then changepage immediately to page one, but this is far from ideal. Any thoughts on that would be appreciated as well.
Here it is:
HTML
<!-- Angular version -->
<button class="ui-btn" onclick="selectTemplate();">My Template</button>
<form>
<div data-role="controlgroup">
<fieldset data-role="controlgroup">
<div ng-controller="templateCtrl">
<label
class="ui-checkbox"
ng-style="{backgroundColor: '{{tagCat.color | bgColor}}'}"
ng-repeat="tagCat in template"><input type="checkbox"
class="ui-checkbox"
id="{{tagCat.name}}"
ng-model="clicked"
ng-click="click();"
/>{{tagCat.name}}</label>
<div ng-repeat="tagCat in template">{{cb}} {{tagCat.name}} hallo</div>
</div>
</fieldset>
</div>
<div style="display:none" class="flashNotification"></div>
</form>
</div>
<div data-role="page" id="two">
<button class="ui-btn" onclick="selectTemplate();">My Template</button>
<form>
<div data-role="controlgroup">
<div ng-controller="templateCtrl">
<div ng-repeat="tagCat in template" ng-show="clicked" class="{{tagCat.name}}">{{tagCat.name}}
<fieldset data-role="controlgroup">
<label class="ui-checkbox"
ng-repeat="item in tagCat.items"
ng-style="{backgroundColor: '{{tagCat.color | bgColor}}'}"
for="item.name">{{tagCat.color | bgColor}}
<input class="ui-checkbox"
name="{{item.name}}"
id='{{item.name}}'
type="checkbox" />{{item.name}}</label>
</fieldset>
</div>
</div>
</div>
<div style="display:none" class="flashNotification"></div>
</form>
</div>
</div>
JS for jQuery Mobile
$(document).ready(function() {
// addTemplateItems(tagTemplate); // not necessary with Angular
// $.mobile.changePage('#two', { transition: 'none' }); // required or checkboxes don't work on load
$.mobile.changePage('#one', { transition: 'none' });
// // $("[data-role=controlgroup]").controlgroup("refresh");
// set up page nav
$(document).delegate('.ui-page', "swipeleft", function(){
var $nextPage = $(this).next('[data-role="page"]');
var $prevPage = $(this).prev('[data-role="page"]');
console.log("binding to swipe-left on "+$(this).attr('id') );
// swipe using id of next page if exists
if ($nextPage.length > 0) {
$.mobile.changePage($nextPage, { transition: 'slide' });
} else {
var message = 'tagged!';
// save tags here
flashNotify(message);
console.log('fire event!');
$('#flashNotification').promise().done(function () {
$('#group1').hide();
$('#group2').hide();
$('.ui-btn').hide();
// addTemplateItems(tagTemplate);
$.mobile.changePage($prevPage, { transition: 'none' });
captureImage();
});
}
}).delegate('.ui-page', "swiperight", function(){
var $prevPage = $(this).prev('[data-role="page"]');
console.log("binding to swipe-right on "+$(this).attr('id') );
// swipe using id of next page if exists
if ($prevPage .length > 0) {
$.mobile.changePage($prevPage, { transition: 'slide', reverse : true });
} else {
alert('no backy backy!');
}
});
// $("input[type='checkbox']").checkboxradio().checkboxradio("refresh");
});
JS for Angular App
var app = angular.module('STL', []);
app.factory('TagTemplate', [function () {
var TagTemplate = {};
var tagTemplate = {
family: {
name: "family",
description: "These are your family members.",
color: "red",
items: [
{
name: "Joe"
},
{
name: "Mary"
},
{
name: "Jim"
}
]
},
design: {
name: "design",
description: "Different types of design notes.",
color: "blue",
items: [
{
name: "inspiring"
},
{
name: "fail"
},
{
name: "wayfinding"
},
{
name: "graphics"
}
]
},
work: {
name: "work",
description: "Stuff for work.",
color: "green",
items: [
{
name: "whiteboard"
},
{
name: "meeting"
},
{
name: "event"
}
]
}
};
TagTemplate = tagTemplate;
return TagTemplate;
}])
// Controller that passes the app factory
function templateCtrl($scope, TagTemplate) {
$scope.template = TagTemplate;
$scope.click = function(model) {
console.log(this.checked, this.tagCat.name);
}
}
app.filter('bgColor', function () {
return function (color) {
// console.log(color, $.Color(color).lightness(.05).toHexString(.05));
// var rgba = $.Color(color).alpha(.05);
return $.Color(color).lightness(.97).toHexString();
}
})
For the main part, success!
I found a jsfiddle that gave me a good base for experimenting. After some playing, I realized that I just have to create a show property within each of the categories in my data service model, and then assign the ng-model to that property to control it.
I had to do it slightly differently in my own code, but the understanding gained from the following jsfiddle led me to the answer:
http://jsfiddle.net/Y43yP/
HTML
<div ng-app ng-controller="Ctrl">
<div class="control-group" ng-repeat="field in customFields">
<label class="control-label">{{field}}</label>
<div class="controls">
<input type="text" ng-model="person.customfields[field]" />
<label><input type="checkbox" ng-model="person.show[field]" /></label>
</div>
</div>
<button ng-click="collectData()">Collect</button><button ng-click="addField()">Add Field</button><br/><br/>
<em>Booleans</em>
<div ng-repeat="field in customFields">
<p>{{field}}: {{person.show[field]}}</p>
</div>
<em>Show/Hide</em>
<div ng-repeat="field in customFields">
<p ng-show="person.show[field]">{{field}}: {{person.customfields[field]}}</p>
</div>
</div>
JS
function Ctrl($scope) {
$scope.customFields = ["Age", "Weight", "Height"];
$scope.person = {
customfields: {
"Age": 0,
"Weight": 0,
"Height": 0
},
show: {
"Age": false,
"Weight": false,
"Height": false
}
};
$scope.collectData = function () {
console.log($scope.person.customfields, $scope.person.show);
}
$scope.addField = function () {
var newField = prompt('Name your field');
$scope.customFields.push(newField);
}
}
Still having the checkbox issue but I'll open a separate issue for that if I can't figure it out.
Thanks.