Aurelia has string interpolation. If you bind a string, number or boolean to that variable, that is bound one-way.
However, if you bind an object and use a ValueConverter, like I want to, it gets bound one-time.
How could I use the value-converter to bind one-way instead of one-time?
I've tried using ${data | objectPrinter & oneWay} but that does not work.
Running code can be found on this gist
app.html
<template>
<div class="row">
<!-- this isn't updated on change -->
<pre>${data | objectPrinter}</pre>
</div>
<input value.two-way="data.branches">
<!-- this gets updated -->
${data.branches}
</template>
app.js
export class App {
data = {
branches: "test"
}
}
export class objectPrinterValueConverter {
toView(obj){
return JSON.stringify(obj, null, 4);
}
}
index.html
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/jspm_packages/system.js"></script>
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/config.js"></script>
<script>
System.import('aurelia-bootstrapper');
</script>
</body>
</html>
Aurelia parses the text of your bind expressions into an abstract syntax tree (AST) and uses it to determine which properties to observe. It only observes the properties that are referenced in the expression.
In your example, the binding expression looks like this: data | objectPrinter. The expression accesses the data property on the view-model. This will make Aurelia observe the data property for changes. But it never changes. In your example, the view model's data property remains the same object instance. The only thing that changes is the branches property which wasn't referenced in the objectPrinter expression, so it's updates don't cause that binding to re-evaluate.
Here's another way to create an object-printer:
https://gist.run/?id=9eea8902521f4523ee2c
Related question (ignore the accepted answer- it won't work in your case):
debug Aurelia ViewModel similar to ko.toJson
Related
I'd like to have an AngularJS component, messageDisplay, which can take a property message (as in, just a propery on its HTML tag in the index.html file) and then display that. Based on all the example code I've been able to find, this should work, but it isn't working.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Angular sandbox</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<script src="app.js"></script>
<script src="components/message-display.component.js"></script>
</head>
<body ng-app="app">
<message-display message="Hi"></message-display>
</body>
</html>
app.js
const app = angular.module("app", []);
message-display.component.js
app.component(
"messageDisplay",
{
bindings: {
message: "<"
},
template: "<h1>Message: {{$ctrl.message}}</h1>"
}
)
I just get a page with the text "Message:" and nothing else. I would expect to get "Message: Hi".
To pass strings to one-way ("<") bindings, use single-quotes:
<message-display message="'Hi'"></message-display>
otherwise it will be evaluated as an AngularJS expression, e.g. $scope.Hi
The DEMO
angular.module("app",[])
.component(
"messageDisplay",
{
bindings: {
message: "<"
},
template: "<h1>Message: {{$ctrl.message}}</h1>"
}
)
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<message-display message="'Hi'"></message-display>
</body>
Update
Where would you recommend I learn this stuff? The whole "bindings" concept and these mysterious <, &, =, # symbols don't seem to be covered in the tutorial
For more informtion, see
AngularJS Comprehensive Directive API Reference - scope
AngularJS Developer Guide - Component-based application architecture
In general, I avoid two-way ("=") binding because it makes the migration to Angular 2+ more difficult.
I also avoid attribute ("#") binding for consistency reasons. I don't need to remember which attributes take an AngularJS expression and which attributes need mustaches ({{ }}).
hey binding should be '#', use this:-
app.component(
"messageDisplay",
{
bindings: {
message: "#"
},
template: "<h1>Message: {{$ctrl.message}}</h1>"
}
)
< one way binding :- when we just want to read a parameter
from a parent scope and not update it.
# this is for String Parameters
const app = angular.module("app", []);
app.component(
"messageDisplay",
{
bindings: {
message: "#"
},
template: "<h1>Message: {{$ctrl.message}}</h1>"
}
)
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
</head>
<body ng-app="app">
<message-display message="Hi"></message-display>
</body>
</html>
<!--
Copyright 2018 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at https://angular.io/license
-->
Mode details blog -> http://blog.krawaller.se/posts/dissecting-bindings-in-angularjs/
I'm learning to use the svelte framework to make components. But I haven't been able to make an instance of only one component from those I have loaded into the js file.
So, this is my html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="./build/ReviewPanelComponent.js"></script>
</body>
</html>
My js input file (where I import the components):
import ReviewPanel from './ReviewPanel.html';
const reviewPanel = new ReviewPanel({
target: document.querySelector( 'body' ),
data:
JSON.parse(localStorage.getItem('rooms'))
});
export default {
components: {
reviewPanel,
}
}
And the html for the component:
<style>
#main{
background-color:white;
}
h3,
h4,
h5{
font-weight: bold;
}
</style>
<div class="container">
<div class="row">
<div id="main" class="col-xs-12">
<div id="title">
{{#if BookedRooms.length > 0}}
{{#each BookedRooms as br}}
<h3>{{br.Title}}</h3>
{{#each br.PossibleRateTypes as prt}}
<h4>{{prt.UUID}}</h4>
{{/each}}
{{/each}}
{{/if}}
</div>
</div>
</div>
</div>
So, at this moment, it loads the component, because it has an instance on the js input file. The thing is, at this moment, if I want a second instance I have to create one of the js file itself. I was thinking more like just importing all the components I want in the js file and in the html file where I want to use them I would just call them.
Is this possible. I've been reading the docs but I haven't found what I want.
EDIT: I forgot to say that I'm using webpack, also. Hence the input and output files. I managed to use multiple components. The solution I've found was to just do this on my js input file:
import ReviewPanel from './ReviewPanel.html';
window.ReviewPanel=ReviewPanel;
window.ReviewPanel2=ReviewPanel;
I put the imported object in a global variable so I can call it where I want it, like so:
<div id="reviewPanel"></div>
<script type="text/javascript">
// Creates the review panel component
window.ReviewPanel = new ReviewPanel({
target: document.querySelector('#reviewPanel'),
data:
JSON.parse(localStorage.getItem('rooms'))
});
</script>
Yet, although I have this solution, my expectation would be import it only once in the js input file, without having to rely on global objects, and then just call it where I need it like so:
// And passing the parameters
<ReviewPanel />
Although I haven't seen much on React.js that's how I generally see programmers doing this. I was expecting the same. Still, as I have other things that need to be done, I'll leave it like this for now, to be optimized later.
Still, if anyone knows how to do this I'm all ears, as I'm new to Svelte and there's not much info on it, apart from the docs, which I consider a bit confusing on same parts.
If I understood correctly, to achieve your goal you could use a module bundler such as Rollup or webpack with their respective Svelte plugin and loader. In this perspective, the easiest way to start is to use a template based on one or the other.
I added the p5 library into the dom like so...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>App</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.10/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.10/addons/p5.dom.js"></script>
</head>
<body>
<div id="app"></div>
<script src="/dist/build.js"></script>
</body>
</html>
So technically I should be able to grab it via window variable within my Vue code...
<template>
<div id="app">
<h1>Hey my app!</h1>
</div>
</template>
<script>
export default {
name: 'app',
mounted() {
console.log(window.p5) // it's found
window.p5.createCanvas(640, 480);
},
}
</script>
Yet error logs show:
TypeError: window.p5.createCanvas is not a function. After logging window.p5, I can see that it's there. Just not createCanvas(). Which makes me think it's not fully there to begin with. Has anyone experienced this problem? How can I successfully import p5 and make use of it in my Vue app?
You can't just randomly call the createCanvas() function. You have to do it after the setup() function is called.
More info here: Why can't I assign variables using p5 functions and variables before setup()?
To fix your problem, you either need to put your call inside the setup() function, or you need to use on-demand instance mode (as explained in the above link) or instance mode (as explained here).
I want to have a single source provide all of my data. A model if you will, and I want my elements to be able to utilize that data, but never change it (one way data-binding). How can I go about this? Should I add the data as a behavior?
I tried doing this in my document:
<script type="text/javascript" src="/data.js"></script> <!-- defines a global object named DATA -->
<my-element data="{{DATA}}"></my-element>
And this inside my-element.html
<dom-module id="my-element">
<template></template>
<script>
Polymer({
is: 'my-element',
properties: {
data: Object
},
ready: function () {
console.log(this.data);
}
});
</script>
</my-element>
but it doesn't seem to work, the value of this.data is literally "{{data}}".
I am looking for a better solution than wrapping the element declaration inside a dom-bind template
To use data binding, you either need to use it inside a polymer element, or inside a dom-bind element. See the explanation here. If you use dom-bind, it's only a case of using the js to set DATA to a property on the dom-bind template element, 'data' maybe, which would be little code.
Essentially, you can't set a global and expect data binding to know about it. You need to either tell dom-bind about it, or the element about it, by setting a property on the element, perhaps using behaviour, as you suggested, or using Mowzer's approach.
An example of using a behaviour would be:
<link rel="import" href="databehaviour.html">
<link rel="import" href="bower_components/polymer/polymer.html">
<dom-module id="an-ele">
<style>
</style>
<template>
<div>{{data.sth}}</div>
</template>
<script>
Polymer({
is: "an-ele",
behaviors: [DataBehaviour]
});
</script>
</dom-module>
With the behaviour being:
<script>
DataBehaviour = {
ready: function() {
this.data = {'sth':'A thing! A glorious thing!'};
}
}
</script>
But in your case, this.data would be set to your DATA global.
Use <iron-meta> [link] or <iron-localstorage>] [link] to share variables between elements or the main document.
I am new to Angularjs. I came across a example online and it got me really confused. Here is the code:
angular.module("testApp",[]).controller("testCtrl", function($scope){
var data = "Hello";
$scope.getData = function(){
return data;
}
$scope.setData = function(newData){
data = newData;
}
});
Here is the view:
<html ng-app = "testApp">
<head>
<script src="lib/Angular.js"></script>
<script src = "foo.js"></script>
</head>
<body ng-controller="testCtrl">
<div ng-click="setData('Hello Hello')">{{getData()}}</div>
</body>
</html>
My question is how does angular know when to trigger the getData() method in the view. The click event will change the data. However its a private variable, not attaching to the $scope, which means $scope does not watch the change of it, then how does angular know when to call the getData() in the view? I know it maybe a dumb question, but please help! thank you so much!!
The double-curly expression is what AngularJS calls an observing directive. During the compilation phase, this directive registers listeners on the expression using the $watch method of the scope.
On the other hand, ng-click is what AngularJS calls a listener directive. This type of directive registers a listener with the DOM instead. Whenever the DOM event fires, the directive executes the associated expression inside an $apply call.
This means that after the click expression is executed, a $digest cycle will begin. In this cycle, the scope examines all the registered $watch expressions (e.g. the double-curly expression containing getData()) and calls the listener in case there's a difference from the previous value.
In the end, it is this digest cycle that ensures that all your bound expressions are evaluated.
The top level controller function runs immediately before it renders the view, in order to initialise the scope. Next the view loads and any logic in the view executes. So when it reaches getData() it returns the output of that function at that time.
The clever part is that Angular automatically binds the data in your views all the way back to the data model, so whenever there is a change in the model (i.e. the source of the data) that automatically updates the value in the view and if necessary will run your getData() method several times.
I saved it here as a Plnkr
Your binding {{getData()}} is a "run on evaluation". So when the DOM renders and angular parses it, it sees the () at the end and runs the function. I'll provide citation in a minute when I find it.
You don't need the getData in angularjs ... or maybe for other uses than the one you are showing right there.
So the right code would be (without getData) :
<html ng-app = "testApp">
<head>
<script src="lib/Angular.js"></script>
<script src = "foo.js"></script>
</head>
<body ng-controller="testCtrl">
<div ng-click="setData('Hello Hello')">{{data}}</div>
</body>
</html>
And with getData :
$scope.getData = function(){
data = 'Hello World';
}
<html ng-app = "testApp">
<head>
<script src="lib/Angular.js"></script>
<script src = "foo.js"></script>
</head>
<body ng-controller="testCtrl">
<div ng-init="getData()" ng-click="setData('Hello Hello')">{{data}}</div>
</body>
</html>