I have an angularjs project and I'm trying to create a page made in angular, using Vuejs.
I've been reading vuejs's documentation but I can't quite figure out how to handle the variables used with angular inside my .twig file.
For instance, if I have something like this:
<section id="ProjectController">
<div class="list-default list-lg">
<a class="animated fade-in-right" v-for="project in projects" ng-href="{% verbatim %}{{ project.url }}{% endverbatim %}">
<project project-on-delete="onDelete" project-show-delete-action="true" project-allow-delete="project.ownerId == user.id" project-show-progress="true" project-show-status="true"></project>
</a>
</div>
</section>
I know ng-controller= id, ng-repeat = v-for
And I have this basic js:
var ProjectController = new Vue({
ready: function () {
this.fetchUsers();
}
el: '#ProjectController',
data: {
projects: null
},
methods:{
getProjects: function(){
this.projects = JSON.parse('array with cluster, id, name, ownerId, etc');
}
},
mounted: function () {
this.getProjects();
}
})
Now, how can I access those variables (Like project or id) inside the json array?
You can simply do:
var projectCtrl = $("#ProjectController").scope();
EDIT
if the `project' directive has isolate scope, then you'll have to enter its scope by using
var projectDirectiveCtrl = $("#ProjectController project").isolateScope();
or
var $project = angular.element("[id='ProjectController']").scope();
since jqLite doesn't support # selector
Related
My project currently is made with VueJS. Now, we need to process a certain template with the input data, then store the result and use it to send an email (for example).
Can i render a template with the user data and save it? how?
I don't want to use another library for this purpose, unless We can't do with VueJS
I have read about SSR. But i don't want to use a server-side rendering. The idea is only render certain messages. Following a behavior like this:
save: function(){
userNote.user = ...
userNote.message = document.getElementById('message').innerHtml;
saveToServer(userNote);
}
The Message template:
<div id="message"> Dear {{user.name}}, please confirm that {{notes}} before {{date}}</div>
I hope i made me understand.
Thanks in advance.
Assuming your template is stored in a component, you could add an export method :
var templateComponent = Vue.component("template-component", {
template: "<p>Hello {{name}}</p>",
props: ["name"],
methods: {
exportHTML: function() {
return this.$el.outerHTML;
}
}
});
var app = new Vue({
el: '#app',
data: {
name: "David",
html: undefined
},
methods: {
getHTML: function() {
this.html = this.$refs.template.exportHTML();
}
}
});
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
<div id="app">
<div style="display: none">
<template-component :name="name" ref="template"></template-component>
</div>
<label>Name
<input v-model="name">
</label>
<button #click="getHTML">Get html</button>
<pre>{{ html }}</pre>
</div>
Then you just have to call the exportHTML method on the template component to retrieve the HTML.
I have defined a controller like this :
app.controller("home", function ($scope, $http, $common) {
$http({
method: "GET",
url: '/posts/loadData'
}).then(function (response) {
//console.clear()
if (typeof response.data.posts != 'undefined') {
console.log(response.data.posts);
$scope.posts = $common.arrangePosts(response.data.posts);
}
});
})
and a service to arrange data :
app.service('$common', function ($timeout, $sce, $httpParamSerializerJQLike) {
var that = this;
this.arrangePosts = function (rawPosts) {
var posts = [];
$.each(rawPosts, function (key, value) {
posts.push({
postId: value.postId,
postLink: '/post/' + that.cleanString(value.title) + '/' + value.postId,
title: value.title,
summary: $sce.trustAsHtml(value.summary)
});
});
return posts;
}
});
using values in html like this :
<div class="widget fullwidth post-single">
<h4 class="widget-title">Latest</h4>
<div class="widget-content">
<ul>
<li ng-repeat="post in posts">
<h4 class="list-title">{{post.title}}</h4>
{{post.summary}}
</li>
</ul>
</div>
</div>
Data coming from server in JSON form :
Object { postId="4", title="asdf", summary="<p>asdf</p>"}
but all the html tags are printing on my page as it is (like a text) in summary.
In many SO posts people suggested to use $sce.trustAsHtml but its not working for me. Please suggest anyway to solve my problem.
Any help will be appreciated..!!
have you tried this?
<div ng-bind-html='post.summary'></div>
You could solve this over a directive. Did you know, that you can use JQuery Lite inside AngularJS to manipulate the DOM?
Here a quick example:
angular.module("PostsDirective",[])
.directive("posts", function($sce){
return {
link: function($scope, $element, $attrs){
//the HTML you want to show
var post = "<div>hello world</div>";
var posts = [post,post,post,post];
//iterating through the list (_.each is a function from underscore.js)
_.each(posts, function(element){
//if you want to save the trusted html string in a var you can do this with getTrustedHtml
//see the docs
var safeHtml = $sce.getTrustedHtml($sce.trustAsHtml(element));
//and here you can use JQuery Lite. It appends the html string to the DOM
//$element refers to the directive element in the DOM
$element.append(safeHtml);
});
}
};
});
And the html
<posts></posts>
This also pretty nice for the readability for your HTML code. And you can use it everywhere on your page.
BTW:
As i can see, you get the HTML elements directly from a REST-Service. Why don't you get just the data and insert it into the ng-repeat? If you transfer all the HTML you get a pretty high overhead if you have loads of data.
I have this markup:
<div data-ng-model="currentUser.attributes">
<div>{{username}}</div>
</div>
And this is a stripped down version of my controller:
$scope.username = "Alice";
$scope.currentUser = {
attributes: {
username: "Bob"
}
};
I want Bob to display, but instead, I am getting Alice. It works just fine if I use this:
{{currentUser.attributes.username}}
But I don't want to have to scope down to this variable's properties every time I want to access something. How can I get the element to exist within the scope of currentUser.attributes?
While I don't think you should really do this, it is what you're asking for. You can essentially mimic with by using ng-repeat on an array that you populate with the relevant object. For example:
<div ng-repeat="user in [currentUser.attributes]">
{{ user.username }}
</div>
Working plunker: http://plnkr.co/edit/svwYEeWMQXjuAnLkr9Vz?p=preview
Other possible solutions would be to have a service or controller that has functions to get the attributes and return them, cleaning up the syntax of your HTML and making it easier to change backend stuff without breaking your frontend. Your choice.
Edit: I noticed you actually expect to be able to do {{ username }} and get the relevant info, if that's really what you want then I suggest my second proposal. Create functions that return the relevant info.
<div>
{{ getCurrentUserName() }}
</div>
$scope.getCurrentUserName = function() {
return $scope.currentUser.attributes.username;
};
Your call, take it or leave it.
If you want Bob just do the the following in your HTML.
<div>{{current user}}</div>//IGNORE THIS
<div>{{currentUser.attributes.username}}</div>//UPDATED CORRECTED
UPDATED based on clarification.
So in Knockout you do this
<p data-bind="with: currentUser.attributes">
<div data-bind="text: userName></div>
<div data-bind="text: login></div>
<div data-bind="text: bhalBlah></div>
<div data-bind="text: yaddaYadda></div>
</p>
<script type="text/javascript">
ko.applyBindings({
currentUser: {
attributes: {
userName : 'Bob',
login : 't#e',
blahBlah : 'ttttt',
yaddaYadda: 'x'
}
}
});
</script>
Same thing in AngularJS would be
<p ng-controller="myCtrl">
<div>{{currentUser.attributes.userName}}</div>
<div>{{currentUser.attributes.login}}</div>
<div>{{currentUser.attributes.blahBlah}}</div>
<div>{{currentUser.attributes.yaddaYadda}}</div>
</p>
<script type="text/javascript">
angular.module('myApp',[]).controller('myCtrl',function($scope){
$scope = {
currentUser: {
attributes: {
userName : 'Bob',
login : 't#e',
blahBlah : 'ttttt',
yaddaYadda: 'x'
}
};
});
</script>
In this the question is how to avoid how not to repeat the part the full property paths between ** as shown below in angular.
**currentUser.attributes.**userName
**currentUser.attributes.**login
**currentUser.attributes.**blahBlah
**currentUser.attributes.**yaddaYadda
Here is one way see plnkr using ng-init which reduces 'currentUser.attributes' to just 'attr'.
With just attr.<properties> repeated
{{attr.userName}}
{{attr.login}}
{{attr.blahBlah}}
{{attr.yaddaYadda}}
Another way is you restructure your object and flatten it on the $scope.
This is not recommended because now you are putting primitives on to the $scope and are widening the scope with $scope.userName = currentUser.attributes.username. Also your 'repetitive' code is still there just in the Javascript.
In lieu of ng-init
ng-init="attr = currentUser.attributes"
You could also do this in controller
$scope.attr = currentUser.attributes;
This post really got me thinking. I had a theory on how to accomplish this using a directive.
Came up with a proof of concept on plnkr: http://embed.plnkr.co/OJDhpJ1maEdSoPvlbiRA/
If I understand correctly, you want to only display the properties within a given block of your struct.
Given the following struct:
$scope.currentUser = {
attributes: {
username: 'Batman',
age: '99',
address: {
street: 'Bat Cave'
}
}
};
You want to scope things down with something like:
<div scope-with="currentUser.attributes">
Username: {{username}}<br />
Age: {{age}}
<div scope-with="address">
Street: {{street}}
</div>
</div>
Directive:
angular.module('mymodule', [])
.directive('scopeWith', function($interpolate){
return {
restrict: 'A',
scope: {
scopeWith: '='
},
transclude: 'element',
compile: function(tElement, tAttrs, linker) {
return function( scope, element, attr) {
var childScope,
parent = element.parent(),
withBlock = null
;
scope.$watch('scopeWith', function(val){
childScope = scope.$new();
angular.forEach(val, function(val, prop){
childScope[prop] = val;
});
if(withBlock) {
withBlock.el.remove();
withBlock.scope.$destroy();
}
linker(childScope, function(clone){
withBlock = {};
parent.append(clone);
withBlock.el = clone;
withBlock.scope = childScope;
});
}, true);
};
}
};
Use {{currentUser.username}} to show Bob.
The ng-model on the div is irrelevant as it only applies to input elements.
I have two ng-app
like ;
<div ng-app="app1" >
somexpression
<div ng-app="app2">
some more expression
</div>
</div>
is there any way to make it work?
when I make a nested ng-app it doesn't work
I know that I can use two different controller but I don't want to use two controllers
---- EDIT -----
The thing is;
angular.module('AppName', [
'angular-carousel'
])
SO I need somehow to change this ng-app to directive
From the AngularJS document, the answer is no
http://docs.angularjs.org/api/ng/directive/ngApp
AngularJS applications cannot be nested within each other.
And if not nested, then it's OK, someone already asked this question, refer here:AngularJS Multiple ng-app within a page
and the AnguarJS document
http://docs.angularjs.org/api/ng/directive/ngApp
Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead.
I found one tricky solution for this problem. The idea is that the "host" application have to somehow jump over the root element of nested application. I used directive for this:
angular.module("ng").directive("ngIsolateApp", function() {
return {
"scope" : {},
"restrict" : "AEC",
"compile" : function(element, attrs) {
// removing body
var html = element.html();
element.html('');
return function(scope, element) {
// destroy scope
scope.$destroy();
// async
setTimeout(function() {
// prepare root element for new app
var newRoot = document.createElement("div");
newRoot.innerHTML = html;
// bootstrap module
angular.bootstrap(newRoot, [attrs["ngIsolateApp"]]);
// add it to page
element.append(newRoot);
});
}
}
}
});
Example simple app:
// module definition
angular.module("testMod1",[])
.service("moduleService", function ModuleService() {
this.counter = 0;
this.getCounter = function() {
return this.counter;
};
this.incCounter = function() {
this.counter += 1;
}
})
.controller("ModuleCtrl", function(moduleService) {
this.getValue = function() {
return moduleService.getCounter();
};
this.incValue = function() {
moduleService.incCounter();
};
});
Now in the markup we can use the ng-isolate-app:
<!-- App instance 1 -->
<body ng-app="testMod1">
<div ng-controller="ModuleCtrl as ctrl">
{{ctrl.getValue()}}
<button ng-click="ctrl.incValue()">Click</button>
<!-- App instance 2 -->
<div ng-isolate-app="testMod1">
<div ng-controller="ModuleCtrl as ctrl">
{{ctrl.getValue()}}
<button ng-click="ctrl.incValue()">Click</button>
<!-- App instance 3 -->
<div ng-isolate-app="testMod1">
<div ng-controller="ModuleCtrl as ctrl">
{{ctrl.getValue()}}
<button ng-click="ctrl.incValue()">Click</button>
</div>
</div>
</div>
</div>
</div>
</body>
Working example on plnkr
This works in simple cases, I do not know how this will work on complex applications.
You can't use one ng-app inside another one in angularjs.
because AngularJS applications cannot be nested within each other.
https://docs.angularjs.org/api/ng/directive/ngApp
I'm trying to learn Backbone.js with Underscore.js and got into trouble. I'm using Grails as a server framework, so the Underscore.js syntax <%= %> is not possible. I want to change it to {{}} style. My Javascripts are separated in many files, each representing either a View or Model for every objects I need. Here is the code for my View:
$(function () {
_.templateSettings = {
interpolate : /\{\{(.+?)\}\}/g,
evaluate : /\{!(.+?)!\}/g
};
APP = window.APP || {};
APP.PlaceView = Backbone.View.extend({
initialize:function () {
this.render();
},
el:"#place-form",
formTemplate:_.template($('#search-template').html()),
render:function () {
//Pass variables in using Underscore.js Template
var variables = { street:"Ulica" };
this.$el.html(this.formTemplate({ "street":"Ulica" }));
}
});
var view = new APP.PlaceView();
});
And the template:
<script type="text/template" id="search-template">
<!-- Access template variables with {{ }} -->
<label>{{ street }}</label>
<input type="text" id="search_input"/>
<input type="button" id="search_button" value="Search"/>
</script>
This code throws Uncaught SyntaxError: Unexpected token ) error. But when I delete the _.templateSettings part, everything is ok, but I don't have the variables.
Thanks a lot.
I believe it's showing that error because of this line:
<!-- Access template variables with {{ }} -->
Underscore is trying to replace that with a variable that doesn't exist, thus throwing an error.