I am new to Ember and am having an issue. I would like the user to be able to select a number of workstations, and when they hit the next button, I would like the controller to create a number of objects equal to the number the user selected. Once they are taken to the next screen I want to view to append a number of divs with the questions equal to the number the user selected.
I have this for the app.js:
//Initialize the application
App = Ember.Application.create({
rootElement: '#main'
});
//Initialize the data model
App.CustomerController = Ember.Object.extend({
first: null,
last: null,
email: null,
phone: null,
clinic: null,
number: null
});
App.Workstation = Ember.Object.extend({
id: null,
title: null,
newOrExisting: null,
cabling: null
});
App.workstationController = Ember.ArrayController.create({
content: [],
num: null,
init: function() {
this.set('content',[]);
var num = this.get('num');
var tempId = Date.now();
var ws = App.Workstation.create({
id: tempId
});
this.pushObject(ws);
}
});
App.selectNoComputers = ["1", "2", "3", "4", "5"];
App.workstationSelect = ["Counter 1", "Counter 2", "Counter 3", "Counter 4", "Office 1", "Office 2", "Office 3"];
App.yesNo = ["New", "Existing"];
App.Router.map(function(match) {
match("/").to("captcha");
match("/customer").to("customer");
match("/wsnum").to("computers");
match("/overview").to("overview");
});
App.CaptchaRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('captcha');
}
});
App.CustomerRoute = Ember.Route.extend();
App.ComputersRoute = Ember.Route.extend();
App.OverviewRoute = Ember.Route.extend({
});
App.initialize();
And this for my html:
<script type="text/x-handlebars" data-template-name="overview">
<div class="navbar">
<div class="navbar-inner">
<div class="progress-bar-label-div">
Progress:
</div>
<div class="progress-bar-div">
<div class="progress progress-striped">
<div class="bar" style="width:60%;"></div>
</div>
</div>
<div class="btn-group pull-right">
{{#linkTo "computers" class="btn"}}
Prev
{{/linkTo}}
</div>
</div>
</div>
<div class="row-a top">
<div class="pull-left" >
<h3>Workstation Overview</h3>
</div>
<div class="pull-right">
</div>
</div>
{{#each App.workstationController}}
<div class="workstation-b">
<div class="row-b">
<div class="pull-left workstation-title" >
<h4>{{id}}</h4>
</div>
<div class="pull-right form-inputs input-text">
<a class="btn btn-primary" >
Start
</a>
</div>
</div>
<div class="row-b">
<div class="pull-left questions">
What station will this be?
</div>
<div class="pull-right form-inputs input-text">
{{view Ember.Select prompt="Please Select" contentBinding="App.workstationSelect"}}
</div>
</div>
<div class="row-b">
<div class="pull-left questions">
Is this computer a new purchase or replacing and existing workstation?
</div>
<div class="pull-right form-inputs input-text">
{{view Ember.Select prompt="Please Select" contentBinding="App.yesNo"}}
</div>
</div>
</div>
{{/each}}
</script>
I'm sure I'm missing something pretty easy, but any help is appreciated.
I put together a fiddle to illustrate my problem.
Working fiddle: http://jsfiddle.net/mgrassotti/xtNXw/3/
Relevant changes: Added an observer to listen for changes to the num property. When it changes, the content array is reset and then an appropriate number of blank workstation objects are created.
App.workstationController = Ember.ArrayController.create({
content: [],
num: null,
init: function() {
this.set('content',[]);
},
addBlankWorkstation: function() {
var tempId = Date.now();
var ws = App.Workstation.create({
id: tempId
});
this.pushObject(ws);
}
});
App.workstationController.addObserver( 'num', function() {
var num = this.get('num');
this.set('content',[]);
for (var i=0;i<num;i++) {
this.addBlankWorkstation();
}
});
I hesitated to say working fiddle above since there are many things that might be worth refactoring. You'll find most of the complexity could be reduced by following ember naming conventions. Suggest looking at latest guides for more detail.
Related
So I'm trying to duplicate the section div (so I can have multiple sections with multiple articles). I tried using the same controller for both divs as shown below. So I'm able to add the section by appending it to main but I can't edit the second div. Is there any way around this?
I am not using bootstrap and i'm using xeditable.
HTML:
<div id="main" ng-app="main">
<div class = "section" ng-controller="newsController">
<h2 id="section" editable-text="sections.section">{{sections.section}}</h2>
<div class = "article" ng-repeat="article in sections.articles">
<h3 id="title" editable-text="article.title"><a editable-text="article.link" href="{{article.link}}">{{article.title}}</a></h3>
<p id="publisher" editable-text="article.publisher">{{article.publisher}}</p>
<p id="orig_title" editable-text="article.orig_title">{{article.orig_title}}</p>
<p id="descr" ng-bind-html="article.description" editable-text="article.description"></p>
</div>
<div class = "section" ng-controller="newsController">
</div>
</div>
JS:
newsletter.controller('newsController',function($scope){
$scope.sections = {
section: "Faculty",
articles: [
{
title: "In __ We Trust",
link:'http://wee.com',
publisher: "Me",
orig_title:"",
description: "Description Here"
}
]
};
$scope.addItem = function(){
$scope.sections.articles.push(this.sections.articles.temp);
$scope.sections.articles.temp={};
};
)};
var newSection = '//Pretend all the tags and the attributes as above are placed here'
$("#add-section").click(function(){
var $section = $('#main').append(newSection);
});
Apologies for formatting. I'm still new to this. Thanks!
Edit: I'm also trying to make this dynamic so the user could edit the texts like the title and the publisher, etc. How would I make the added section also editable?
Try it this way, instead of appending it uses angulars natural way of repeating divs aka ng-repeat:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.sections = {
section: "Faculty",
articles: [{
title: "In __ We Trust",
link: 'http://wee.com',
publisher: "Me",
orig_title: "",
description: "Description Here"
}]
};
$scope.addItem = function() {
$scope.sections.articles.push(this.sections.articles.temp);
$scope.sections.articles.temp = {};
};
var newSection = '//Pretend all the tags and the attributes as above are placed here'
$("#add-section").click(function() {
var $section = $('#main').append(newSection);
});
});
</script>
<div id="main" ng-app="myApp" ng-controller="myCtrl">
<div class="section" ng-repeat="i in ['0','1']">
<h2 id="section" editable-text="sections.section">{{sections.section}}</h2>
<div class="article" ng-repeat="article in sections.articles">
<h3 id="title" editable-text="article.title"><a editable-text="article.link" href="{{article.link}}">{{article.title}}</a></h3>
<p id="publisher" editable-text="article.publisher">{{article.publisher}}</p>
<p id="orig_title" editable-text="article.orig_title">{{article.orig_title}}</p>
<p id="descr" ng-bind-html="article.description" editable-text="article.description"></p>
</div>
<div class="section" ng-controller="myCtrl"></div>
</div>
I Got the answer! So I applied the app to the html document and the controller to the body as opposed to the main div and made an array of sections as opposed to a singular section. I did a ng-repeat for the section div. By doing so, I added a "addsection" function where I create a section to be added to the array and the section has to have the same properties as the other ones including an empty array of articles.
HTML:
<body ng-controller="newsController">
<ul id="convenient-buttons">
<li id="add-section">Add Section</li>
<li>Select All</li>
<li><a href=# id="export" onclick="selectText('json')" ng-mouseenter="showJson=true" ng-mouseleave="showJson=false" >Send to Server</a></li>
</ul>
<div id="main">
<div class = "section" ng-repeat="section in news.sections" >
<h2 id="section" editable-text="sections.section">{{sections.section}}</h2>
<div class = "article" ng-repeat="article in sections.articles">
<h3 id="title" editable-text="article.title"><a editable-text="article.link" href="{{article.link}}">{{article.title}}</a></h3>
<p id="publisher" editable-text="article.publisher">{{article.publisher}}</p>
<p id="orig_title" editable-text="article.orig_title">{{article.orig_title}}</p>
<p id="descr" ng-bind-html="article.description" editable-text="article.description"></p>
</div>
</div>
</body>
JS:
$scope.news = {
sections: [
{
title: "Faculty",
articles: [
{
title: "In Memoriam: Eli Pearce",
link:'http://engineering.nyu.edu/news/2015/05/29/memoriam-eli-pearce',
publisher: "NYU Newsroom",
orig_title:" ",
description: "When <strong>Professor Eli Pearce</strong> passed away on May 19, 2015, a slice of Poly history passed along with him. Pearce had been affiliated with the school, then known as the Polytechnic Institute of Brooklyn, since the mid-1950s, when he conducted his doctoral studies in chemistry here. As a student, he learned with such luminaries as Herman Frances Mark, who is often called the Father of Polymer Science, and Charles Overberger, another influential chemist who helped establish the study of polymers as a major sub-discipline."
}
]
}
]
};
$scope.addSection = function(){
$scope.news.sections.temp={
title: "Section Title",
articles:[
// {
// title:"Article Title",
// link:"Link",
// publisher: "Publisher",
// orig_title: "Original Title",
// description: "Description"
// }
]
};
$scope.news.sections.push(this.news.sections.temp);
};
I have set up a fiddle to explain my question well. I would like to display the names from the $scope.gem inside ng-repeat [only one name for each ng-repeat and don't loop all] of $scope.knobItems without extending the knobItems scope. I want this to be made possible by maintaining the exact structure of controller as it is now. I am new to angular. I just wanna know if this is possible in angular and if is a good practice.
view
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="knobs in knobItems">
<div ng-repeat="(key, value) in knobItems.nums">{{value.knobTitle}} : {{value.knobColor}}
<div ng-bind="gem[0].name"></div>
</div>
</div>
</div>
</div>
controller
var ngMod = angular.module("myapp", []);
ngMod.controller("Mycont", function ($scope) {
$scope.knobItems = {};
$scope.knobItems.nums = [{
knobTitle: "Company Profile",
knobColor: "#f46607"
}, {
knobTitle: "Deals left This Month",
knobColor: "#ffcc00"
}, {
knobTitle: "Pricelist",
knobColor: "#f40787"
}, {
knobTitle: "Pictures",
knobColor: "#a1b80a"
}, {
knobTitle: "Videos",
knobColor: "#14b9d6"
}];
$scope.gem = [{
name: "Thomas"
}, {
name: "Sebastian"
}, {
name: "June"
}, {
name: "Yuvan"
}];
});
intended output
Easy fix: fiddle
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="knobs in knobItems">
<div ng-repeat="(key, value) in knobItems.nums">{{value.knobTitle}} : {{value.knobColor}}
<div ng-bind="gem[$index].name"></div>
</div>
</div>
</div>
</div>
The output in you fiddle is exactly the same without the first ng-repeat: http://jsfiddle.net/2nrbrfxL/
Going by you description rather than you code:
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="knobs in knobItems">
<div ng-repeat="(key, value) in knobs">{{value.knobTitle}} : {{value.knobColor}}
<div ng-repeat="gemItem in gem">{{gemItem.name}}</div>
</div>
</div>
</div>
</div>
http://jsfiddle.net/p2fuq2du/
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="(key, value) in knobItems.nums">{{value.knobTitle}} : {{value.knobColor}}
<div ng-bind="gem[key].name"></div>
</div>
</div>
</div>
I have a multidimensional array from an API. Is it possible to programatically loop through the array?
{
success: true,
categories: [{
cat_id: "2",
name: "This is category One",
description: null,
photo_url: "/img/test.png",
order: "1",
items: [{
item_id: "1",
title: "Desk",
item_url: "/690928460",
photo_url: "/desk.png",
}, {
item_id: "2",
title: "Chair",
item_url: "/18882823",
photo_url: "/chair.png",
},
}]
}]
}
My controller looks like this:
myApp.controller('items', function($scope, $http, $location, Data) {
var apiUrl = '/api/items';
$http.get(apiUrl).
success(function(data) {
$scope.data = Data;
$scope.allData = data;
});
$scope.changeView = function(view) {
$location.path(view);
}
});
Angular index file just has: <div ng-view=""></div>
View file
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="row" ng-repeat="categories in allData">
<div class="col-xs-6" ng-repeat="category in categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
</div>
I can loop through the category names fine, but when trying to return items for EACH category I don't understand the logic behind it...
I would suggest some simple nested for loops, as for each gives rise to more complexity.
As I'm not sure what you want to do with the data let's just create an array of all item names and one of all category names:
Within your success function:
var items = [], categories = []
for(var i = 0; i < data.categories.length;i++){
categories.push(data.categories[i].name);
for(var j = 0; j < data.categories[i].items.length;j++){
items.push(data.categories[i].items[j].name);
}
}
console.log(categories);
console.log(items);
EDIT:
Completely missed your html code somehow, here is my solution:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
EDIT 2:
As to your comment:
If you want to select the secondary view's contents(ie the items) based on the selection of a category I would suggest a ng-click attribute. A directive could be used but isn't necessary:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="title" ng_click="selected_category = category">
{{ category.name }}
</div>
</div>
<div class="col-xs-6" ng-repeat="item in selected_category.items">
<div class="title">
{{ item.name }}
</div>
</div>
</div>
</div>
So when your categories data is loaded the first ng-repeat is populated with the categories. Each div with class title will have a function called on click which will make the selected_category object equal the selected category.
This will then cause the second view to be populated with all the items in the selected category by Angular's two way bind.
I'm just getting started with Angular and running up against some roadblocks in my understanding of certain core concepts. To better familiarize myself with this new framework I'm attempting to build a trivial application: "Would You Rather?". I present the user two questions, they pick one, I highlight their choice, and show how many votes each question has from previous users.
It sounds simple but I'm still stuck in a jQuery frame of mind; I want to select the element based on $(this) or $("#id").
I have a factory with an array of question objects. Each object has a firstQuestion and secondQuestion key that maps to a question, as well as a firstVotes and secondVotes key with the corresponding number of votes. I'm using a QuestionsCtrl to control scope and take action when a user makes a choice.
Here's my index.html file:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>Would You Rather?</title>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container" ng-app="wouldYouRatherApp">
<div ng-controller="QuestionsCtrl">
<div class="container">
<div class="row text-center">
<h1 class="col-md-12">Would you rather...</h1>
</div>
<div class="row text-center">
<div class="col-md-12">
<h2 class="btn btn-lg btn-{{buttonClass}}" ng-click="recordAnswer('first')">{{question.firstQuestion}}</h2>
<span class="badge" ng-show="badge.show">{{question.firstVotes}}</span>
</div>
</div>
<div class="row text-center">
<h3 class="col-md-12"><small>or</small></h3>
</div>
<div class="row text-center">
<div class="col-md-12">
<h2 class="btn btn-lg btn-{{buttonClass}}" ng-click="recordAnswer('second')">{{question.secondQuestion}}</h2>
<span class="badge" ng-show="badge.show">{{question.secondVotes}}</span>
</div>
</div>
<br>
<div class="row text-center">
<div class="col-md-12">
<h2 class="btn btn-lg btn-primary" ng-show="showNextQuestion" ng-click="nextQuestion()">Next Question <span class="glyphicon glyphicon-forward"></span></h2>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
And here's my main.js file:
var app = angular.module("wouldYouRatherApp", []);
app.factory("Badges", function() {
return {show: false}
})
app.factory("Questions", function() {
var Questions = [{
firstQuestion: "Ride a horse?",
firstVotes: 101,
secondQuestion: "Ride a cowboy?",
secondVotes: 212
},
{
firstQuestion: "Kiss a frog?",
firstVotes: 13,
secondQuestion: "Lick a slug?",
secondVotes: 23
},
{
firstQuestion: "Play Monopoly?",
firstVotes: 12,
secondQuestion: "Play Risk?",
secondVotes: 17
}];
return Questions;
})
app.controller("QuestionsCtrl", function($scope, Badges, Questions) {
$scope.question = Questions.shift();
$scope.buttonClass = 'default';
$scope.showNextQuestion = false;
$scope.badge = Badges;
$scope.recordAnswer = function(choice) {
console.log("User chose: " + choice);
$scope.buttonClass = 'success';
// increment votes badge
$scope[choice+'Votes'] += 1;
Badges.show = true;
$scope.showNextQuestion = true;
}
$scope.nextQuestion = function() {
$scope.question = Questions.shift();
Badges.show = false;
$scope.buttonClass = 'default';
$scope.showNextQuestion = false;
}
})
A live example can be found here: http://jsfiddle.net/t99TL/2/
The app's expected behavior is as follows:
Two questions are presented to the user.
The user clicks on one of the buttons.
That question is highlighted. And the votes badge is incremented. Both votes badges are displayed.
A 'Next Question' button is presented to the user.
When he/she clicks on the 'Next Question' button, a new question is loaded.
I feel like I probably need to create a directive for each individual question... but I'm not sure how to start, or if I'm even on the right path. Any advice on obstacles I'm going to face further down the line is much appreciated (i.e. Updating the votes attribute for the question, etc.).
There are a lot to change to make it work the way you want. This code is not perfect, just a demonstration.
HTML:
<div class="container" ng-app="wouldYouRatherApp">
<div ng-controller="QuestionsCtrl">
<div class="container">
<div class="row text-center">
<h1 class="col-md-12">Would you rather...</h1>
</div>
<div class="row text-center">
<div class="col-md-12">
<h2 class="btn btn-lg btn-{{question.firstQuestion.buttonClass}}" ng-click="recordAnswer(question.firstQuestion)">{{question.firstQuestion.text}}</h2>
<span class="badge" ng-show="badge.show">{{question.firstQuestion.votes}}</span>
</div>
</div>
<div class="row text-center">
<h3 class="col-md-12"><small>or</small></h3>
</div>
<div class="row text-center">
<div class="col-md-12">
<h2 class="btn btn-lg btn-{{question.secondQuestion.buttonClass}}" ng-click="recordAnswer(question.secondQuestion)">{{question.secondQuestion.text}}</h2>
<span class="badge" ng-show="badge.show">{{question.secondQuestion.votes}}</span>
</div>
</div>
<br>
<div class="row text-center">
<div class="col-md-12">
<h2 class="btn btn-lg btn-primary" ng-show="showNextQuestion" ng-click="nextQuestion()">Next Question <span class="glyphicon glyphicon-forward"></span></h2>
</div>
</div>
</div>
</div>
JS:
var app = angular.module("wouldYouRatherApp", []);
app.factory("Badges", function() {
return {show: false}
})
app.factory("Questions", function() {
var Questions = [{
firstQuestion: {
text:"Ride a horse?",
votes: 101,
buttonClass : 'default'
},
secondQuestion: {
text:"Ride a cowboy?",
votes: 212,
buttonClass : 'default'
},
},
{
firstQuestion: {
text:"Kiss a frog?",
votes: 13,
buttonClass : 'default'
},
secondQuestion: {
text:"Lick a slug?",
votes: 23,
buttonClass : 'default'
}
},
{
firstQuestion: {
text:"Play Monopoly?",
votes: 12,
buttonClass : 'default'
},
secondQuestion: {
text:"Play Risk?",
votes: 17,
buttonClass : 'default'
}
}];
return Questions;
})
app.controller("QuestionsCtrl", function($scope, Badges, Questions) {
$scope.question = Questions.shift();
$scope.buttonClass = 'default';
$scope.showNextQuestion = false;
$scope.badge = Badges;
$scope.recordAnswer = function(choice) {
choice.buttonClass = 'success';
choice.votes++;
Badges.show = true;
$scope.showNextQuestion = true;
}
$scope.nextQuestion = function() {
$scope.question.firstQuestion.buttonClass = "default";
$scope.question.secondQuestion.buttonClass = "default";
$scope.question = Questions.shift();
Badges.show = false;
$scope.showNextQuestion = false;
}
})
DEMO
I'm making a "members" page for a website, which has 3 columns of pictures and names for each member. I want to be able to click on the picture to get more information about that member (via Foundation's modal). I'm using a repeater to display the pictures, but I'm stuck on how to pass member data into the modal so that when I click on person A, the modal will have person A's data.
Here is what I have so far:
I store the members as a json list in members.json:
{
"current" : [
{
"name": "Bob",
"pic": "http://www.placehold.it/300x300",
"id": "blah",
"position": "position 1",
"bio": "Hi I am Bob"
},
{
"name": "Bobby",
"pic": "http://www.placehold.it/300x300",
"id": "blah",
"position": "position 2",
"bio": "Hi I am Bobby"
}
]
}
And here is my controller, uses the json file:
var app = angular.module('app').controller('memberCtrl', ['$scope', '$http', function ($scope, $http) {
'use strict';
$http.get('/data/members.json').success(function(data) {
$scope.members = data;
$scope.numColumns = 3;
$scope.current_rows = [];
$scope.current_rows.length = Math.ceil($scope.members.current.length / $scope.numColumns);
$scope.current_cols = [];
$scope.current_cols.length = $scope.numColumns;
});
}]);
And here is my html:
<!-- My attempt at the modal -->
<div ng-controller="memberCtrl" id="myModal" class="reveal-modal">
<div class="row">
<div class="large-3 columns">
<img src="http://www.placehold.it/300x300">
</div>
<div class="large-6 columns">
<center>
<!-- The following remains blank -->
<h2>{{members.current[$parent.$index * numColumns + $index].name}}</h2>
</center>
<p class="lead">Bio.</p>
<p>Hi</p>
</div>
<div class="large-3 columns">
<img src="http://www.placehold.it/300x300">
</div>
</div>
<a class="close-reveal-modal">×</a>
</div>
<!-- This creates a 3 column array of member profiles (it works) -->
<div ng-controller="memberCtrl">
<div class="row" data-ng-repeat="row in current_rows">
<div class="large-4 columns" data-ng-repeat="col in current_cols">
<img ng-src="{{members.current[$parent.$index * numColumns + $index].pic}}">
<center>
<small><h3>{{members.current[$parent.$index * numColumns + $index].name}}</h3></small>
<small><h4>{{members.current[$parent.$index * numColumns + $index].position}}</h4></small>
</center>
</div>
</div>
</div>
Add ng-click attr in the img:
<img ng-src="{{members.current[$parent.$index * numColumns + $index].pic}}"
ng-click="loadMember(members.current[$parent.$index * numColumns + $index]);"/>
And add the function in the controller:
$scope.loadMember = function (member) {
//you should be able to access the current member
console.log(member);
}
Demo on jsFiddle