How to load JSON array to translate AngularJS webpage with Angular-Translate - javascript

I am still learning AngularJS heavily so pardon all my bad quality code/lack of knowledge.
What I want to do
I want to translate my webpage into 2 languages. It currently works when I translate static content using Angular-Translate in the index.html. Like this
function translateConfiguration($translateProvider) {
$translateProvider.useSanitizeValueStrategy(null);
$translateProvider.useStaticFilesLoader({
files: [{
prefix: '../JSON/locale-',
suffix: '.json'
}]
});
$translateProvider.preferredLanguage('en');
$translateProvider.fallbackLanguage('en');
};
I load my simple JSON files for EN and RU locales, structured like this
{
"HEADING" : "Lorem",
"TEXT" : "Ipsum"
}
Then I access the variables through Angular-translate directive
<h1 class="z-logo header-text md-display-1" translate="HEADING">
</h1>
All works as it should.
I want to be able to do the same with my custom directives and ng-repeat.
The issue
I have multiple custom directives in my AngularJS website. For instance
<about-card flex="33" flex-xs="100" flex-sm="100" class="center-content" ng-repeat="data in aboutCardCtrl.info">
</about-card>
Here is the template code that loads through the custom directive and retrieves information from the JSON file
<md-card class="card-bg card-height">
<md-card-title layout="column" class="about-card">
<md-card-title-media class="center-elements">
<img class="md-media-lg" ng-src="{{data.image}}" alt="{{data.imageAlt}}"/>
</md-card-title-media>
<md-card-title-text>
<div class="md-headline card-title-padding">{{data.heading}}</div>
</md-card-title-text>
</md-card-title>
<md-card-content>
{{data.content | translate}}
</md-card-content>
</md-card>
Here is the directive
(function() {
'use strict'
angular
.module('webApp')
.directive('aboutCard', aboutCard);
function aboutCard() {
return {
restrict: 'EA',
priority: 1001,
templateUrl: '../TEMPLATES/about.html',
controller: 'aboutCardController',
controllerAs: 'aboutCardCtrl'
};
};
})();
Here is the controller
(function() {
'use strict'
angular
.module('webApp')
.controller('aboutCardController', aboutCardController);
aboutCardController.$inject = ['JsonData', '$translate'];
function aboutCardController(JsonData, $translate) {
var vm = this;
vm.pathToJson = '../JSON/about-en.json';
vm.info = [];
JsonData.all(vm.pathToJson).then(function(response) {
vm.info = response.data.information;
});
};
})();
Here is the JSON file
{
"information": [{
"heading": "Reliability",
"content": "Our partners have been working on the market for over 10 years",
"image": "../IMG/shield.svg",
"imageAlt": "Reliability Image"
}, {
"heading": "Professionalism",
"content": "We are ready to provide profesional opinion for our clients to ensure the right choice",
"image": "../IMG/people.svg",
"imageAlt": "Professionalism Image"
}, {
"heading": "Development",
"content": "Organization of educational programs in collaboration with our partners",
"image": "../IMG/cogwheel.svg",
"imageAlt": "Development Image"
}]
}
Here I suppose I lack the brainpower to understand how I can load my JSON files and switch between them in the custom directive with ng-repeat. Since The format of the JSON is different.
I've been going through Angular-Translate wiki pages, but all examples feature only index.html and main app.js files and I hardly seem to be able to find/understand examples found in google search like these
https://technpol.wordpress.com/2013/11/02/adding-translation-using-angular-translate-to-an-angularjs-app/
https://github.com/angular-translate/angular-translate/issues/1154

You can use dynamic loader instead of static.
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'JSON/{part}/{lang}.json'
});
And then load JSON that you need like this:
$translatePartialLoader.addPart('about');
Language prefix will be automatically add to the path.

Related

AngularJS How to use RESTful with factory module

I have been getting an error to make RESTful code for 2 hours.
I have one file has a controller here
angular.module('orderToBeShippedApp',['ng','projectService'])
.controller('orderCtrl',function($scope, Project){
Project.query(function(data){
alert(data);
$scope.projects = data;
});
});
Here is my fatory which is called 'project'.
angular.module('projectService',['ng','ngResource'])
.factory('Project',function($resource){
return $resource('api/url/:projectId.json',{},{
query:{
method:'get',
params: {projectId:"test"},
isArray: false
}
});
});
My html:
<div ng-controller = "orderCtrl" class ="col-sm-8 col-md-9">
<!--Body content -->
<div ng-repeat = "project in projects">
<h2>{{project.name}}</h2>
</div>
</div>
My json is something like
[
{
"age": 0,
"id": "a",
"imageUrl": "a0.jpg",
"name": "b",
"snippet": "Tbaa."
},
{
"age": 1,
"id": "mo",
"imageUrl": "assets/images/phones/mo.0.jpg",
"name": "M\u2122",
"snippet": "The Ned by Android 3.0 (Honeycomb)."
}
]
It doesn't give me any error message on the browser. it seems having a problem on factory function.
Could anyone help me to fix it?
You should change your binding to be name, not ProjectName based on your model definition:
<div ng-controller = "orderCtrl" class ="col-sm-8 col-md-9">
<!--Body content -->
<div ng-repeat = "project in projects">
<h2>{{project.name}}</h2>
</div>
Also, to use ngResource you need to remember to add a script reference to: angular-resource:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular-resource.min.js"></script>
UPDATE:
Remove 'ng' Dependency
Pass the resource to the controller as a dependency.
// resource
angular.module('projectService',['ngResource'])
.factory('Project',function($resource){
return $resource('api/url/:projectId.json',{},{
query:{
method:'get',
params: {projectId:"test"},
isArray: false
}
});
});
// controller
angular.module('orderToBeShippedApp',['projectService'])
.controller('orderCtrl',['$scope','Project', function($scope, Project){
Project.query(function(data){
alert(data);
$scope.projects = data;
});
}]);
Your factory may not be giving a console error however it may not be able to bring back the data. Your factory should be looking like this:
angular.module('projectService',['ng','ngResource'])
.factory('Project',function($resource){
return {
projects: $resource('api/url/:projectId.json', {projectId: '#test'})
}
});
}]);
In your controller it should invoke the method like this:
angular.module('orderToBeShippedApp',['ng','projectService'])
.controller('orderCtrl',['$scope','Project',function($scope, Project){
$scope.projects = Project.projects.get({id: 'test'});
}]);
UPDATE:
In order to relax chrome the xmlHttpRequest error you need to run this command from the chrome.exe folder of your machine:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --allow-file-access-from-files --disable-web-security

Dynamic form in AngularJS

I'm developing a CMS for a customer, it's all based on AngularJS with its controllers, views, services, etc.
What I need is a pattern where a dynamically loaded script injects some data in an existing scope.
Ok, in human words: I have a form managed by a controller. This form has several preset fields. These fields are managed by an array of the scope, something like:
$scope.fields = [
{ type: "text", name="first_name" },
{ type: "text", name="last_name" },
{ type: "email", name="email" }
];
The view prints dynamically the fields (i.e. it's a scaffolding).
When the customer log into the application I check if in his profile he has a custom script to load, if so the application appends a javascript to the DOM, the javascript file name is equal to the username of the logged user.
So, if the user is called "darko" and he has a custom script enabled, the application append this file to the DOM:
/js/customers/darko.js
Let's say that darko has further fields to show (and save) inside the form, how can I do that? I'd need to hook the controller so I can have access to its scope and then inject my fields. Something like:
var $scope = getUserFormScope();//some magic....
$scope.fields.push({ type: "text", name="skype" });
However, The form with further fields is just an example, what I really need, more generally, is a way to "hook controllers" and have access to theirs scopes.
Any idea?
SOLUTION
I've finally used the method suggested by marfarma. The custom script contains one or more partial controllers named with the same name of the controller they want to extend prefixed by Custom word, then I extend my controllers with these partial controllers. For example, my app has a controller named PageController, inside this controller I check if a CustomPageController exists:
if (typeof CustomPageController == 'function') {
angular.extend(this, CustomPageController($scope));
}
if so, I extend the main controller with the custom one.
Here is a general way to "hook controllers" and have access to their scopes - mixin your hook code via angular.extend.
function CustomUserController($scope) {
// contents of this hook controller is up to you
var _this = this;
// Mixin instance properties.
this.vocalization = getValue('vocalization',$scope.user);
this.runSpeed = getValue('runSpeed' ,$scope.user);
// Mixin instance methods.
this.vocalize = function () {
console.log(this.vocalization);
};
// Mixin scope properties.
$scope.color = color;
// Mixin scope methods.
$scope.run = function(){
console.log("run speed: " + _this.runSpeed );
};
}
function PageController($scope) {
var _this = this;
$scope.user; // this should be the current user obj, with key for custom script lookup
// Mixin Custom Script into Controller.
if (userService.hasCustomScript($scope.user)) {
angular.extend(this, new CustomUserController($scope));
}
}
As for your specific example, one way to insert arbitrary fields into a form is to build it dynamically. I use a schema form directive that might work for your situation. Given a schema that defines the model properties, and an array that specified the items their order of inclusion, the directive lays out the form.
For example (see also this working plunker, incl. add'l features):
<form class="form-inline" name="form" novalidate role="form">
<div class="row-fluid clearfix">
<h2>Sample Form</h2>
</div>
<div class="row-fluid clearfix">
<hr>
<div class="span12">
<fieldset class="span6">
<schema-form-fields
fields="side1Fields"
model="model"
schema="modelSchema"
data="requestResult"
schema-list="schema">
</schema-form-fields>
</fieldset>
<fieldset class="span6">
<schema-form-fields
fields="side2Fields"
model="model"
schema="modelSchema"
data="requestResult"
schema-list="schema">
</schema-form-fields>
</fieldset>
</div>
</div>
<div class="row-fluid clearfix">
<button
class="btn btn-primary span2 offset10"
type="submit">
Submit
</button>
</div>
</form>
// example controller with dynamic form
app.controller('HomeCtrl', ['$scope', 'schema', 'requestResult', 'dataService',
function($scope, schema, requestResult, dataService) {
$scope.modelSchema = schema.product;
$scope.model = {
factoryDate: '20160506'
};
// field name arrays - one array per column in sample layout
// insert elements into these and form should re-render
// may require explicit watch to trigger update
$scope.side1Fields = [
'productName',
'factoryDate'
];
$scope.side2Fields = [
'productType',
'timeValue'
];
// .... other controller code
}
]);
// example schema
app.value('schema', {
"product": {
"type": "object",
"title": "Product Schema",
"properties": {
"productType": {
"type": "string",
"title": "Product Type",
"showLabel": true,
"tooltip": "Product classification",
"readonly": false,
"required": true,
"class": "custom-select",
"enum": ['Bike', 'Car', 'Airplane', 'Glider', 'Stilts']
},
"productName": {
"title": "Product Name",
"showLabel": true,
"type": "string",
"tooltip": "A more descriptive name for the modeled structure.",
"readonly": false,
"required": true
},
"factoryDate": {
"title": "Factory Date",
"type": "string",
"showLabel": true,
"control": "date",
"dateFormat": "yyyymmdd", // TODO format as provided
"tooltip": "Date of manufacture.",
"dateOptions": {
autoclose: true
},
"readonly": false,
"required": true
},
"timeValue": {
"title": "Time (HHMM)",
"showLabel": true,
"type": "string",
"pattern": "([0-1]{1}[0-9]{1}|20|21|22|23)[0-5]{1}[0-9]{1}",
"timeFormat": "hhmm", // TODO format as provided
"tooltip": "Time entry.",
"readonly": false,
"required": true,
}
}
}
});

AngularJS assign variables to view's scope

I have a somewhat deep JSON object I am trying to using in an HTML template.
{
"service": {
"name": "example",
"url": "abc.com",
"template": "/abc/def/v1",
"metadata": {
"password": "dontguessme",
"username": "supereasy"
}
}
}
I am including a template with the following HTML code.
<div class="modal-body" ng-include="service.instructionsTemplate"> </div>
In the template there is the following.
<h1>Some example content</h1>
{{service.metadata.password}}
My question is instead of referencing the field password via service.metadata, is there a way I can reference it with just the variable password.
I was trying to dig through some of the Angular docs around scoping and templates but came up empty. I saw you can use ng-init.
I was able to use ng-init="metadata = service.metadata" and was able to reference the field password in the template via metadata.password.
However I would just like to reference it by password.
Any ideas?
You already did ng-init="metadata = service.metadata", why not going a step further and do ng-init="password = service.metadata.password"?
Another way would be to bind $scope.password = service.metadata.password inside the controller thayou're using on that page
Edit: OP asked a more general solution
If you don't know the property names, then your best move would be to bind the metadata object, like you already did, and then iterate through its properties using ng-repeat
in your controller:
$scope.metadata = service.metadata
in your template (view):
<h1>Some example content</h1>
<ul>
<li ng-repeat="element in metadata">{{element}}</li>
</ul>
You can easily set the password to something inside the controller:
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.data = {
"service": {
"name": "example",
"url": "abc.com",
"template": "/abc/def/v1",
"metadata": {
"password": "dontguessme",
"username": "supereasy"
}
}
};
$scope.password = $scope.data.service.metadata.password;
});
Here is a demo: http://plnkr.co/edit/KrKeLIP2ANd0rl5NLywF?p=preview

How do I include a .js file in html loaded with a json?

I need to display an instance of a javascript class that's loaded with data from .json file with using require.js in backbone.
I have the following js and json files:
collections/Companies.js
define([
'models/Company'
], function(CompanyModel) {
'use strict';
var CompanyCollection = Backbone.Collection.extend({
model: CompanyModel
});
return CompanyCollection;
});
data/companies.json
[{
"id": 1000001,
"name": "Test Company 1",
"description": "this is a test company that should be displayed"
},
{
"id": 1000002,
"name": "Test Company 2",
"description": "this is another test company that should be displayed as well"
}]
Also, I have the following code snippet:
<script type="text/javascript" data-main="data/companies.json" src="collections/Companies.js"></script>
<script type="text/javascript">
require(["collections/Companies"],
function(Companies){
$("#json").append(Companies[0].id);
alert("test");
}
);
</script>
<div id="json"></div>
Of course it is not working as I intended and I can't figure out the correct syntax and/or iteration logic.
How can I load the .js class with the data from the .json file?
My <script type="text/javascript" data-main... line doesn't seem to work.
Thanks in advance and sorry if I'm not clear enough.
requirejs is usually used to load scripts and template files but you can use it to load a .json file as well. but you have to parse it yourself using JSON.parse.
You have 2 different options,
using requirejs text plugin, like:
require(["collections/Companies", "text!data/companies.json"],
function(Companies, CompaniesJSONStr) {
var CompaniesJSON = JSON.parse(CompaniesJSONStr);
}
);
or using requirejs plugins, which it again uses text! plugin:
require.config({
paths : {
json: 'data'
}
});
define([ 'collections/Companies', 'json!companies.json' ],
function(Companies, CompaniesJSON){
}
);

Angularjs - NgView not working

Here is the basic setup, which has a default noemployee.html partial: as the ng-view
Index.html content:
<div id="container" ng-controller="EmployeeCtrl">
<!-- Side Menu -->
<span id="smenuSpan">
<ul id="thumbList">
<li ng-repeat="employee in employees | filter:categories">
<img class="smallImage" ng-src="content/app/images/{{employee.image}}" alt="{{employee.description}}">
</li>
</ul>
</span>
<!-- Content -->
<span id="contentSpan">
<div ng-view></div>
</span>
</div>
My Route Provider:
var EmployeeModule = angular.module('EmployeeModule', [], function($routeProvider, $locationProvider) {
$routeProvider.when('/', { templateUrl: 'content/app/partials/noemployee.html', controller: EmployeeModule.EmployeeCtrl });
$routeProvider.when('Employee/:id', { templateUrl: 'content/app/partials/employee.html', controller: EmployeeModule.EmployeeCtrl });
$routeProvider.otherwise({ redirectTo: '/' });
$locationProvider.html5Mode(true);
My Controller:
function EmployeeCtrl($scope, $http, $routeParams, $timeout) {
$scope.employees = [
{ "id": 1, "category": "ones", "image": "person1.jpg", "description": "person 1 description", name:"Jane Smith" },
{ "id": 2, "category": "twos", "image": "person2.jpg", "description": "person 2 description", name: "Mark Sharp" },
{ "id": 3, "category": "threes", "image": "person3.jpg", "description": "person 3 description", name: "Kenny Suave" },
{ "id": 4, "category": "fours", "image": "person4.jpg", "description": "person 4 description", name: "Betty Charmer" },
{ "id": 5, "category": "fives", "image": "person5.jpg", "description": "person 5 description", name: "John Boss" }
];
$scope.employeesCategories = [];
$scope.currentEmployee = {};
$scope.params = $routeParams;
$scope.handleEmployeesLoaded = function (data, status) {
//$scope.images = data;
// Set the current image to the first image in images
$scope.currentEmployee = _.first($scope.employees);
// Create a unique array based on the category property in the images objects
$scope.employeeCategories = _.uniq(_.pluck($scope.employees, 'category'));
}
$scope.fetch = function () {
$http.get($scope.url).success($scope.handleEmployeesLoaded);
};
$scope.setCurrentEmployee = function (employee) {
$scope.currentEmployee = employee;
};
// Defer fetch for 1 second to give everything an opportunity layout
$timeout($scope.fetch, 1000);
}
Observations:
At present, if I click on any employee, no 'Employee/??' is added to the address bar path [ which isn't a crime to me], however, the main content div does not change the partial to the employee.html.
If I comment out "$locationProvider.html5Mode(true);", the default localhost is now "http://localhost:31219/#/" and when I click on any employee the address bar shows 'http://localhost:31219/Employee/1', and the page is navigated away to a 404 error page.
I know I am bastardizing something here that the solution is so simple it escapes me.
Goals:
I really would like to avoid hash tags in my address bar.
It would be nice but no req that the employee/id not show up in the address bar but I suspect the partial cannot change w/o it. and, naturally
I want the partial to change to the 'employee.html" page when an employee is clicked.
Does anyone see where I am going wrong with this code?
Thanks in Advance!
Solution:
I needed to put '#/' in the img href --> href="#/Employee/{{employee.id}}"
Comment out
'$locationProvider.html5Mode(true);'
As a side note, I sure wish I knew how to get this to work w/o those pesky hash tags. Any ideas anyone?
In order to use html5mode, your server has to serve up the main app index file for otherwise invalid routes.
So, for example, if your server side code handles CRUD operations on paths like: /api/employees, /api/employees/:id, etc...
and it serves up static content, images, html, css, js, etc.
For any other request, that would otherwise be a 404, it should, instead of responding with a 404, respond with a 200 code, and serve up the index.html file.
This way any non static and non server side route gets handled by the angular app.
This is mentioned in the Angular guide on this page: http://docs.angularjs.org/guide/dev_guide.services.$location
Note the 'server side' comment at the end:
Html link rewriting
When you use HTML5 history API mode, you will need
different links in different browsers, but all you have to do is
specify regular URL links, such as: link
When a user clicks on this link:
In a legacy browser, the URL changes to /index.html#!/some?foo=bar
In a modern browser, the URL changes to /some?foo=bar In cases like the
following, links are not rewritten; instead, the browser will perform
a full page reload to the original link.
Links that contain target element
Example: link
Absolute links that go to a different domain
Example: link
Links starting with '/' that lead to a different base path when base is defined
Example: link
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite
all your links to entry point of your application (e.g. index.html)
This was the problem:
<img class="smallImage" ng-src="content/app/images/{{employee.image}}" alt="{{employee.description}}">
Solution:
I needed to put '#/' in the img href --> href="#/Employee/{{employee.id}}"
Comment out '$locationProvider.html5Mode(true);'
As a side note, I sure wish I knew how to get this to work w/o those pesky hash tags. Any ideas anyone?

Categories

Resources