Can't set properties in ember after promise succeeds - javascript

When the promise succeeds and I attempt to set a property of the controller using this.set('propertyName', value) I get the following error regarding the set method:
TypeError: undefined is not a function
Template:
<script type="text/x-handlebars">
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Qlovi Reader</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<form class="navbar-form navbar-left" role="search"{{action 'updateInfo' on="submit" }}>
<div class="form-group">
{{input value=attemptQuestionSet type="text" class="form-control" placeholder="37297"}}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
{{#if currentUser}}
<p class="navbar-text navbar-right">Signed in as {{currentUser.userName}}</p>
{{/if}}
</div>
</div>
</nav>
<div class="container-fluid">
{{outlet}}
</div>
</script>
Application Controller:
App.ApplicationController = Em.Controller.extend({
currentQuestionSet: null,
actions:{
updateInfo: function(){
this.store.find('questionSet', parseInt(this.attemptQuestionSet)).then(function(questionSet){
this.set('currentQuestionSet',questionSet);
},function(){
alert("Question Set not found");
});
}
}
});
Is there a new scope once the promise succeeds or other issues regarding scope?

In general, you have to assume callbacks (not just promises, all callbacks) will lose the value of the this pointer unless documentations specifically says otherwise. This behavior is because the caller is what determines what this points to, and in this case the caller is the then function in whatever promise library Ember is using.
The solution is to capture this in a closure, like so:
App.ApplicationController = Em.Controller.extend({
currentQuestionSet: null,
actions:{
updateInfo: function(){
var self = this; // Store away current this
this.store.find('questionSet', parseInt(this.attemptQuestionSet)).then(function(questionSet){
self.set('currentQuestionSet',questionSet); // <-- note use of stored reference
},function(){
alert("Question Set not found");
});
}
}
});

Related

Inheritance scope using ui.router to filter data in ng-repeat [Angular1.5]

I have a problem probably with inheritance scope using ui.router. I'm creating a small application something like addressbook. I want to have an input in navbar which allow me to filter addresses on other page (so when i'm writing there, the parial view should be changed by ui-router). I have one view named index.html and other tamplets for each of sites using ui-ruter.
When i'm writing in input, view is changing, but the data is not filtered. I didn't receive any errors. Thanks for answers.
(Sorry for complicated description, its my first post)
index.html (without header with links)
<body ng-app="appModule" ng-controller="applicationController">
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#nav-toggle">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand">Contact Manger</a>
</div>
<div class="collapse navbar-collapse" id="nav-toggle">
<ul class="nav navbar-nav">
<li><a ui-sref="index">View</a></li>
<li><a ui-sref="add">Add Contact</a></li>
</ul>
<form class="navbar-form navbar-right" role="search">
<input type="text" class="form-control" placeholder="Search" ng-model="search" ng-keyup="startSearch()">
</form>
</div>
</nav>
<div class="container">
<ui-view>
</ui-view>
</div>
appModule.js
angular.module("appModule",[
"ui.router",
"AddModule",
"DetailsModule",
"MainModule",
"ngSanitize"])
appController.js
angular.module("appModule")
.controller('applicationController', function($scope, $location){
$scope.startSearch = function(){
$location.path('index');
};
});
router.js
angular.module('appModule')
.config(function($stateProvider){
$stateProvider.state('index',{
url: '/index',
template: "<main-component></main-component>"
})
.state('add',{
url: '/add',
template: "<add-component></add-component>"
})
.state('contact-details',{
url: '/details/:id',
template: "<details-component></details-component>",
})
})
mainController.js (its in other module)
(function () {
function MainController($scope, contactFactory) {
$scope.contacts = contactFactory.get();
}
angular.module("MainModule")
.component("mainComponent", {
templateUrl: "/assets/partials/main.html",
controller: ["$scope", "contactFactory", MainController],
})
})()
And half of code in ui-router template:
<tbody>
<tr ng-repeat="contact in contacts | filter:search">
<td>{{contact.name}}</td>
<td>{{contact.email}}</td>
<td>{{contact.phone}}</td>
<td><a ui-sref="contact-details({id:$index})" class="btn btn-default btn-xs">More..</a></td>
</tr>
</tbody>
As you might know, $scope has prototype inheritance you can change your code to something like this:
just assign search to a property of an object on scope like this;
$scope.data = {};
$scope.data.search = '';
your input changes to this:
<input type="text" class="form-control" placeholder="Search" ng-model="data.search" ng-keyup="startSearch()">
and finally your inner component becomes like this:
<tr ng-repeat="contact in contacts | filter:data.search">
<td>{{contact.name}}</td>
<td>{{contact.email}}</td>
<td>{{contact.phone}}</td>
<td><a ui-sref="contact-details({id:$index})" class="btn btn-default btn-xs">More..</a></td>
</tr>
Here Misko discusses this issue more deeply.

Sorting by property of JSON objects in AngularJS with ng-repeat

I'm having trouble sorting JSON objects based on their properties. I referred to this stackoverflow answer https://stackoverflow.com/a/18186947/3496380 and it's comparing the actual property's value in its sort function yet the objects are still not sorted on the page (although they ARE in a different order, just not alphabetical)
Here's the relevant code:
<body ng-app="app" ng-controller="mainController">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">xxxxxxx</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div class="starter-template">
<h1>xxxxxx</h1>
<p class="lead">xxx</p>
</div>
<div class="container-fluid thumbnail">
<div class="col-xs-2" ng-repeat="champ in champions | orderObjectBy:'name'">
<div class="">
<h6>{{ champ.name }}</h6>
<img ng-src="http://ddragon.leagueoflegends.com/cdn/{{ versionNumber }}/img/champion/{{ champ.image.full }}" />
</div>
</div>
</div>
</div><!-- /.container -->
The app.js file:
var app = angular.module('app', ['ui.bootstrap']).controller('mainController', function($scope, $http, $sce) {
// champion names json
$scope.urlChampions = "https://na.api.pvp.net/api/lol/static-data/na/v1.2/champion?champData=blurb,image&api_key=KEY";
// grab list of champion names
$http.get($scope.urlChampions).then(function(response) {
$scope.champions = response.data.data;
$scope.versionNumber = response.data.version;
});
$scope.trust = $sce.trustAsHtml;
});
app.filter('orderObjectBy', function(){
return function(input, attribute) {
if (!angular.isObject(input)) return input;
var array = [];
for(var objectKey in input) {
array.push(input[objectKey]);
}
array.sort(function(a, b){
//console.log(typeof a);
a = a[attribute];
b = b[attribute];
//console.log(typeof a);
//console.log(a + " fsdfa " + b);
//console.log(a < b);
return a < b;
});
return array;
}
});
Here's a link to the JSON array that's being grabbed: http://pastebin.com/V7Jb3xpn
EDIT: Got it to work by changing the array.sort function in app.js to the following
array.sort(function(a, b){
a = a[attribute];
b = b[attribute];
if (a < b) {
return -1;
}
else if (a > b) {
return 1;
}
else {
return 0;
}

Koa-handlebars: Unable to render view: The partial could not be found

// project/layouts/main.hbs
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
{{{#body}}}
</body>
</html>
// project/views/home-public.hbs
{{> nav-public}}
<div class="container">
<div class="starter-template">
<h1>Home Public</h1>
<p class="lead">This is my home.</p>
</div>
</div>
// project/partials/nav-public.hbs
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Example</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active">Journey
</li>
<li>Departures
</li>
<li>About
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>Sign in
</li>
</ul>
</div>
</div>
</nav>
Node code that runs:
app.use(handlebars({
defaultLayout: 'main'
}));
app.use(function* () {
yield this.render('home-public', {
user: {
email: "name#example.com"
}
});
});
I can't see what's wrong. Any ideas?
This one caught me out at first, too. The reason is that you're using hyphens and/or path separators in your {{> partial-name}} calls, which seems reasonable when you're coming from express-handlebars. The answer is in the documentation:
partialId(file)
This function is a little backwards compared to layouts and views, but it takes a path for a partial template file. (relative to partialsDir) and converts it into a handlebars-friendly identifier.
For example: "navigation.hbs" => "navigation"
By default, it will strip the extension and camel-case the remaining string.
For example: "nav/main.hbs" => "navMain"
So basically, by default it's going to translate your partial path completely into camelCaseForHyphensAndDirectorySlashes, for example example/my-partial would require a partial call of {{> exampleMyPartial }}.
This is fortunately fairly easy to customise if you prefer to use a partial name that actually represents your partial's file path. Here's the config I use (I use the strip-extension module to get rid of the file extension):
var stripExtension = require('strip-extension');
app.use(koaHandlebars({
partialId: function(file) {
// Note: the .replace below is just to normalise windows paths, you
// may not need it.
return stripExtension(file).replace('\\', '/');
}
});

AngularJS : call a Controller method with ng-click [duplicate]

I have a simple loop with ng-repeat like this:
<li ng-repeat='task in tasks'>
<p> {{task.name}}
<button ng-click="removeTask({{task.id}})">remove</button>
</li>
There is a function in the controller $scope.removeTask(taskID).
As far as I know Angular will first render the view and replace interpolated {{task.id}} with a number, and then, on click event, will evaluate ng-click string.
In this case ng-click gets totally what is expected, ie: ng-click="removeTask(5)". However... it's not doing anything.
Of course I can write a code to get task.id from the $tasks array or even the DOM, but this does not seem like the Angular way.
So, how can one add dynamic content to ng-click directive inside a ng-repeat loop?
Instead of
<button ng-click="removeTask({{task.id}})">remove</button>
do this:
<button ng-click="removeTask(task.id)">remove</button>
Please see this fiddle:
http://jsfiddle.net/JSWorld/Hp4W7/34/
One thing that really hung me up, was when I inspected this html in the browser, instead of seeing it expanded to something like:
<button ng-click="removeTask(1234)">remove</button>
I saw:
<button ng-click="removeTask(task.id)">remove</button>
However, the latter works!
This is because you are in the "Angular World", when inside ng-click="" Angular all ready knows about task.id as you are inside it's model. There is no need to use Data binding, as in {{}}.
Further, if you wanted to pass the task object itself, you can like:
<button ng-click="removeTask(task)">remove</button>
Also worth noting, for people who find this in their searches, is this...
<div ng-repeat="button in buttons" class="bb-button" ng-click="goTo(button.path)">
<div class="bb-button-label">{{ button.label }}</div>
<div class="bb-button-description">{{ button.description }}</div>
</div>
Note the value of ng-click. The parameter passed to goTo() is a string from a property of the binding object (the button), but it is not wrapped in quotes. Looks like AngularJS handles that for us. I got hung up on that for a few minutes.
this works. thanks. I am injecting custom html and compile it using angular in the controller.
var tableContent= '<div>Search: <input ng-model="searchText"></div>'
+'<div class="table-heading">'
+ '<div class="table-col">Customer ID</div>'
+ ' <div class="table-col" ng-click="vm.openDialog(c.CustomerId)">{{c.CustomerId}}</div>';
$timeout(function () {
var linkingFunction = $compile(tableContent);
var elem = linkingFunction($scope);
// You can then use the DOM element like normal.
jQuery(tablePanel).append(elem);
console.log("timeout");
},100);
Above answers are excellent. You can look at the following full code example so that you could exactly know how to use
var app = angular.module('hyperCrudApp', []);
app.controller('usersCtrl', function($scope, $http) {
$http.get("https://jsonplaceholder.typicode.com/users").then(function (response) {
console.log(response.data)
$scope.users = response.data;
$scope.setKey = function (userId){
alert(userId)
if(localStorage){
localStorage.setItem("userId", userId)
} else {
alert("No support of localStorage")
return
}
}//function closed
});
});
#header{
color: green;
font-weight: bold;
}
<!DOCTYPE html>
<html>
<head>
<title>HyperCrud</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body>
<!-- NAVBAR STARTS -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">HyperCrud</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
<li class="dropdown">
Apps<span class="caret"></span>
<ul class="dropdown-menu">
<li>qAlarm »</li>
<li>YtEdit »</li>
<li>GWeather »</li>
<li role="separator" class="divider"></li>
<li>WadStore »</li>
<li>chatsAll</li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>Login</li>
<li>Register</li>
<li>Services<span class="sr-only">(current)</span></li>
</ul>
</div>
</div>
</nav>
<!--NAVBAR ENDS-->
<br>
<br>
<div ng-app="hyperCrudApp" ng-controller="usersCtrl" class="container">
<div class="row">
<div class="col-sm-12 col-md-12">
<center>
<h1 id="header"> Users </h1>
</center>
</div>
</div>
<div class="row" >
<!--ITERATING USERS LIST-->
<div class="col-sm-6 col-md-4" ng-repeat="user in users">
<div class="thumbnail">
<center>
<img src="https://cdn2.iconfinder.com/data/icons/users-2/512/User_1-512.png" alt="Image - {{user.name}}" class="img-responsive img-circle" style="width: 100px">
<hr>
</center>
<div class="caption">
<center>
<h3>{{user.name}}</h3>
<p>{{user.email}}</p>
<p>+91 {{user.phone}}</p>
<p>{{user.address.city}}</p>
</center>
</div>
<div class="caption">
DELETE
UPDATE
</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<a href="/regiser/">
<img src="http://img.bhs4.com/b7/b/b7b76402439268b532e3429b3f1d1db0b28651d5_large.jpg" alt="Register Image" class="img-responsive img-circle" style="width: 100%">
</a>
</div>
</div>
</div>
<!--ROW ENDS-->
</div>
</body>
</html>
HTML:
<div ng-repeat="scannedDevice in ScanResult">
<!--GridStarts-->
<div >
<img ng-src={{'./assets/img/PlaceHolder/Test.png'}}
<!--Pass Param-->
ng-click="connectDevice(scannedDevice.id)"
altSrc="{{'./assets/img/PlaceHolder/user_place_holder.png'}}"
onerror="this.src = $(this).attr('altSrc')">
</div>
</div>
Java Script:
//Global Variables
var ANGULAR_APP = angular.module('TestApp',[]);
ANGULAR_APP .controller('TestCtrl',['$scope', function($scope) {
//Variables
$scope.ScanResult = [];
//Pass Parameter
$scope.connectDevice = function(deviceID) {
alert("Connecting : "+deviceID );
};
}]);
Here is the ng repeat with ng click function and to append with slider
<script>
var app = angular.module('MyApp', [])
app.controller('MyController', function ($scope) {
$scope.employees = [
{ 'id': '001', 'name': 'Alpha', 'joinDate': '05/17/2015', 'age': 37 },
{ 'id': '002', 'name': 'Bravo', 'joinDate': '03/25/2016', 'age': 27 },
{ 'id': '003', 'name': 'Charlie', 'joinDate': '09/11/2015', 'age': 29 },
{ 'id': '004', 'name': 'Delta', 'joinDate': '09/11/2015', 'age': 19 },
{ 'id': '005', 'name': 'Echo', 'joinDate': '03/09/2014', 'age': 32 }
]
//This will hide the DIV by default.
$scope.IsVisible = false;
$scope.ShowHide = function () {
//If DIV is visible it will be hidden and vice versa.
$scope.IsVisible = $scope.IsVisible ? false : true;
}
});
</script>
</head>
<body>
<div class="container" ng-app="MyApp" ng-controller="MyController">
<input type="checkbox" value="checkbox1" ng-click="ShowHide()" /> checkbox1
<div id="mixedSlider">
<div class="MS-content">
<div class="item" ng-repeat="emps in employees" ng-show = "IsVisible">
<div class="subitem">
<p>{{emps.id}}</p>
<p>{{emps.name}}</p>
<p>{{emps.age}}</p>
</div>
</div>
</div>
<div class="MS-controls">
<button class="MS-left"><i class="fa fa-angle-left" aria-hidden="true"></i></button>
<button class="MS-right"><i class="fa fa-angle-right" aria-hidden="true"></i></button>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="js/multislider.js"></script>
<script>
$('#mixedSlider').multislider({
duration: 750,
interval: false
});
</script>

Why can't I change this emberjs model without an error?

I'm writing an ember app that has this settings model that I'm using to bypass routing because I want a single url. However, I get this error:
Error: Cannot perform operations on a Metamorph that is not in the DOM.
when I change any of the data in the model. I'm relatively new to ember, but from what I read of what's out there, you should be able to change a model just fine. Here's my html file:
<script type="text/x-handlebars" data-template-name="index">
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="#"><img src="./img/MySpending.png"></a>
<div class="nav-collapse">
<ul id="navWrapper" class="nav">
{{#if model.isIndex}}
{{partial "indexNav"}}
{{/if}}
{{#if model.isMonthly}}
{{partial "monthlyNav"}}
{{/if}}
{{#if model.isYearly}}
{{partial "yearlyNav"}}
{{/if}}
</ul>
</div>
<div class='navbar_form pull-right'></div>
</div>
</div>
</div>
<div id="bodyWrapper" class="container" style="padding-top:60px;">
{{#if model.isIndex}}
{{partial "indexPage"}}
{{/if}}
{{#if model.isMonthly}}
{{partial "monthlyPage"}}
{{/if}}
{{#if model.isYearly}}
{{partial "yearlyPage"}}
{{/if}}
</div>
{{outlet}}
And each partial works fine initially.
It's when I change it in the three regular functions in my app.js that I have a problem. Here's my app.js:
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.settings=Ember.Object.extend({
isIndex: true,
isMonthly: false,
isYearly: false
});
Settings=App.settings.create();
//App.SettingsController = Ember.ObjectController.extend(Settings); Not sure whether to put this in or not
App.IndexRoute = Ember.Route.extend({
model: function() {
return Settings;
}
});
function goToMonthly(){
Settings.set('isIndex',false);
Settings.set('isMonthly',true);
Settings.set('isYearly',false);
}
function goToYearly(){
Settings.set('isIndex',false);
Settings.set('isMonthly',false);
Settings.set('isYearly',true);
}
function goToIndex(){
Settings.set('isIndex',true);
Settings.set('isMonthly',false);
Settings.set('isYearly',false);
}
So I'm not sure where the error is but if anyone can help,it'd be extremely appreciated.
There's a fundamental error in your approach here. Your Route/Controller are meant to handle the model for you, so by declaring your model outside of these objects, you're making more work for yourself and leading yourself to potential trouble.
I'm not seeing where your "goTo" functions are being called (I assume they're inside the partials, but if they're in that HTML and I missed it, that's my bad). But here's how they should be referenced:
App.SettingsController = Ember.ObjectController.extend({
actions:{
goToMonthly:function(){
this.set('isIndex', false);
this.set('isMonthly', true);
this.set('isYearly', false);
},
//Other functions go here
}
});
Then within your html, call these actions using handlebars:
<a {{action 'goToMonthly'}}> Click this to run the action </a>
Which ember version are you using? I tried it with ember 1.2 and 1.3 and your example is working fine.

Categories

Resources