AngularJS two controllers... overcoming $scope.$on when page loads - javascript

Still wrapping my head around AngularJS. I have a pretty basic page which has two controllers. One has text input to take tags (for searching). The other calls http.get(), using the tags to get the images... it does a bit of ordering and then they're displayed. Following some info on the page below, I came up w/ something.
Can one controller call another?
Caveat: It requires onClick() to execute. Generally I want it to load up on page load with zero tags... and after a click with the tags. I've seen a few suggestions in other threads here, but I'm not quite sure how to pull them off in this case. imagesController runs on load, but of course doesn't get past the 'handleBroadcast' bit.
var myModule = angular.module('myModule', []);
myModule.run(function($rootScope) {
$rootScope.$on('handleEmit', function(event, args) {
$rootScope.$broadcast('handleBroadcast', args);
});
});
function tagsController($scope) {
$scope.handleClick = function(msg) {
$scope.tags = msg;
$scope.$emit( 'handleEmit', { tags: msg });
};
}
function imagesController($scope,$http) {
$scope.$on('handleBroadcast', function(event, args) {
$scope.tags = args.tags;
var site = "http://www.example.com";
var page = "/images.php";
if ( args.tags ) {
page += "?tags=" + $scope.tags;
}
$http.get(site+page).success( function(response) {
// SNIP
$scope.data = response;
});
});
}
The HTML is quite trivial. Slightly simplified, but it should suffice.
<form name="myForm" ng-controller="tagsController">
<div class="left_column">
<input class="textbox_styled" name="input" data-ng-model="tags"><br>
<button ng-click="handleClick(tags);">Search</button><br>
</div>
</form>
<div class="centered" data-ng-controller="imagesController">
<span class="" data-ng-repeat="x in data">
{{x}}
</span>
</div>

Not sure I fully understood, but if you want to show it with no tags on load, simply emit the event when the controller loads:
function tagsController($scope) {
$scope.handleClick = function(msg) {
$scope.tags = msg;
$scope.$emit( 'handleEmit', { tags: msg });
};
$scope.$emit( 'handleEmit', { tags: "" }); // or $scope.handleClick("");
}

Related

Changes in scope variable not updating DOM

I came across this angularJs issue where a change in an array is not updating the DOM, even though there is a binding in place.
I have tried everything from using $scope.$apply, $scope.$evalAsync and also tried using vm instead of $scope.
AngularJs code:
app.controller("controller", function ($scope){
$scope.managers = [];
$scope.RefreshManagerList = async function(){
var response = await App.getStoreManagers();
$scope.managers = response;
};
$scope.Setup = async function(){
await App.start();
await $scope.RefreshManagerList();
};
$scope.Setup();
});
HTML Code:
<header>
<h1>Administrator Panel</h1>
</header>
<div class="jumbotron">
<h3>Store Manager List</h3>
<ul class="list-group">
<li class="list-group-item" ng-repeat="manager in managers">
{{manager}}
</li>
</ul>
</div>
What is expected to happen is that when $scope.managers is set to response, the list should be rendered in the HTML page.
EDIT:
Tried doing this also doesn't work. Console.log still gives the expected object to be displayed in the list. If I interact with the page in the slightest way, the list automatically loads:
app.controller("administratorController", function ($scope){
$scope.managers = {content: null};
$scope.RefreshManagerList = function(){
App.getStoreManagers().then(function(response){
$scope.managers.content = response;
console.log($scope.managers.content);
});
};
$scope.Setup = function(){
App.start().then(function(){
$scope.RefreshManagerList();
});
};
$scope.Setup();
});

How to use the same HTML render different content via different urls?

I have an HTML page and multiple URLs as follows
var app = angular.module('app', []);
app.controller('index', ($scope,$http) => {
$http.get('/get_js').then((res) => {
$scope.cat = res.data;
}, () => {
alert('database error');
});
});
/get_js
/get_node
/get_ng
......
I expect if I query different url, such as $http.get('/get_js') or $http.get('/get_node') or $http.get('/get_ng') it will render different content on the same HTML.
For example, I have several links. If I click on 'a'link, HTML will render some content via '/get_js'. If I click on 'b'link, HTML will render some content via '/get_ng' and so on. Each time, I will click one link.
Could anyone tell me how can I do that?
Thank you very much!
PS: I am using an express server to host the content.
Then you can take a function and make appropriate api call in that function when a link is clicked by sending the parameter.
Steps:
Write a funciton which calls each api dynamically depending on the
parameter.
Send the api url as a parameter from the respective anchot tag to
that function.
Make the api call in that function and show the response in html
Here is how the function may look, I commented your API call.
$scope.linkClicked = function(link)
{
// $http.get('/get_js').then((res) => {
// $scope.data = res.data;
// }, () => {
// alert('database error');
// });
$scope.data = link + ' clicked';
}
Code:
var app = angular.module('myApp', []);
app.controller('index', function($scope) {
$scope.linkClicked = function(link)
{
// $http.get('/get_js').then((res) => {
// $scope.data = res.data;
// }, () => {
// alert('database error');
// });
$scope.data = link + ' clicked';
}
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="index">
<a ng-click="linkClicked('get_js')"> Link1 </a>
<a ng-click="linkClicked('get_node')"> Link2 </a>
<a ng-click="linkClicked('get_ng')"> Link3 </a>
<p ng-if="data"> {{data}} </p>
</div>
</body>
</html>
Please run the above snippet
Here is a working DEMO

Is that possible to put Template7 code in a separate file rather than in html

I am using a framework called Framework7.
In my index.html, I have some Template7 code, like this format
<script type="text/template7" id="commentsTemplate">
{{#each this}}
<div> test this template 7 code </div>
</script>
However, I want to have this part of code into an another separated file (Just like I can have many other *.js files in, say, a static folder and refer to the file by "static/*.js).
I have tried to use a typical way to import js
<script type="text/template7" id="storiesTemplate" src="js/template.js"></script>
But it doesn't work, there is also no demo/sample code in the documentation.
Any help is appreciated!
You can do it. The idea behind is to include a HTML file in a HTML file. I can tell at least 3 ways that this can happen, but personally I fully validated only the third.
First there is a jQuery next sample is taken from this thread
a.html:
<html>
<head>
<script src="jquery.js"></script>
<script>
$(function(){
$("#includedContent").load("b.html");
});
</script>
</head>
<body>
<div id="includedContent"></div>
</body>
</html>
b.html:
<p> This is my include file </p>
Another solution, I found here and doesn't require jQuery but still it's not tested: there is a small function
My solution is a pure HTML5 and is probably not supported in the old browsers, but I don't care for them.
Add in the head of your html, link to your html with template
<link rel="import" href="html/templates/Hello.html">
Add your template code in Hello.html. Than use this utility function:
loadTemplate: function(templateName)
{
var link = document.querySelector('link[rel="import"][href="html/templates/' + templateName + '.html"]');
var content = link.import;
var script = content.querySelector('script').innerHTML || content.querySelector('script').innerText;
return script;
}
Finally, call the function where you need it:
var tpl = mobileUtils.loadTemplate('hello');
this.templates.compiledTpl = Template7.compile(tpl);
Now you have compiled template ready to be used.
=======UPDATE
After building my project for ios I found out that link import is not supported from all browsers yet and I failed to make it work on iphone. So I tried method number 2. It works but as you might see it makes get requests, which I didn't like. jquery load seems to have the same deficiency.
So I came out with method number 4.
<iframe id="iFrameId" src="html/templates/template1.html" style="display:none"></iframe>
and now my loadTemplate function is
loadTemplate: function(iframeId, id)
{
var iFrame = document.getElementById(iframeId);
if ( !iFrame || !iFrame.contentDocument ) {
console.log('missing iframe or iframe can not be retrieved ' + iframeId);
return "";
}
var el = iFrame.contentDocument.getElementById(id);
if ( !el ) {
console.log('iframe element can not be located ' + id );
return "";
}
return el.innerText || el.innerHTML;
}
How about lazy loading and inserting through the prescriptions?
(function (Template7) {
"use strict";
window.templater = new function(){
var cache = {};
var self = this;
this.load = function(url)
{
return new Promise(function(resolve,reject)
{
if(cache[url]){
resolve(cache[url]);
return true;
}
if(url in Template7.templates){
resolve(Template7.templates[url]);
return true;
}
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if(this.status == 200 && this.response.search('<!DOCTYPE html>') == -1){
cache[url] = Template7.compile(this.response);
resolve(cache[url]);
}else{
reject(`Template ${url} not found`);
}
};
xhr.send();
})
}
this.render = function(url, data)
{
return self.load(url)
.then(function(tpl){
return tpl(data) ;
});
}
this.getCache = function()
{
return cache;
}
}
})(Template7);
Using :
templater.render('tpl.html').then((res)=>{ //res string })
Or :
templater.load('tpl.html').then( tpl => { Dom7('.selector').html( tpl(data) ) } )
It is possible to define your templates in .js-files. The template just needs to be a string.
Refer to this [JSFiddle] (https://jsfiddle.net/timverwaal/hxetm9rc/) and note the difference between 'template1' and 'template2'
var template1 = $$('#template').html();
var template2 = '<p>Hello, my name is still {{firstName}} {{lastName}}</p>'
template1 just extracts the content of the <script> and puts it in a string.
template2 directly defines the string

Dynamically add content in Angular with ajax

I have a page on a website based on AngularJS on which content gets loaded via ajax into a div. My problem is that the Angular directives in the loaded content and the controller seem to get ignored. How can I make the controller working?
HMTL:
Load Content
<div id="myForm-con">
</div>
<script src="js/angular.min.js"></script>
<script src="js/app.js"></script>
<script type="text/javascript">
$('#loadContent').on('click', function() {
$.post("load-my-form.php", function(data, result) {
$('#myForm-con').html(data);
});
});
</script>
app.js
(function() {
var app = angular.module('app', []);
app.controller('validationCtrl', ['$scope',
function ($scope) {
$scope.submitForm = function () {
$scope.submitted = true;
if ($scope.myForm.$valid) {
alert('form is valid');
}
}
}
]);
})();
HTML ajax loaded form
<div ng-controller="validationCtrl">
<form id="my-form" name="myForm" ng-submit="submitForm()" novalidate>
...
</form>
</div>
I'd probably try something like this:
HTML
Load Content
Angular
$scope.loadContent() = $http.post("load-my-form.php", data).then(function (result) {
$('#myForm-con').html(result);
});
Or alternatively
Angular
$scope.formResults = '';
$scope.loadContent() = $http.post("load-my-form.php", data).then(function (result) {
$scope.formResults = result;
});
HTML
<div id="myForm-con">{{formResults}}</div>
In order for this to work you have to do 2 things:
1 - use angular http and .then for the callback -
$http.post('/someUrl', data, config).then(successCallback, errorCallback);
2- use angular data-binding to link your data to a view, for example - 2-ways data-binding.

Definition of Function SubmitSurvey have not been found but I defined it

It is literally fifth day I try to solve this.
I try to invoke a method by a button in Razor View, no redirections to other views, just invoke a simple method when button is clicked.
The script looks like:
<script>
function SubmitClick () {
var pid = $(this).data('personid');
var sid = $(this).data('surveyid');
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { personid: pid, surveyid: sid }, function (data) {
alert('updated');
});
};
</script>
The button looks like:
<button class='mybutton' type='button' data-personid="#Model.Item1.Id" data-surveyid="#survey.Id" onclick="javascript:SubmitClick()">Click Me</button>
The PersonController method looks like:
public void SubmitSurvey(int personId, int surveyId) {
System.Diagnostics.Debug.WriteLine("UPDATING DATABASE");
}
The full view (this is PartialView):
<script>
function SubmitClick () {
var pid = $(this).data('personid');
var sid = $(this).data('surveyid');
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { personid: pid, surveyid: sid }, function (data) {
alert('updated');
});
};
</script>
#using WebApplication2.Models
#model System.Tuple<Person, List<Survey>>
<hr />
<h1>Surveys</h1>
<input type="button" id="Coll" value="Collapse" onclick="javascript:CollapseDiv()" />
#*<p>
Number of Surveys: #Html.DisplayFor(x => Model.Item2.Count)
</p>*#
#{int i = 1;}
#foreach (var survey in Model.Item2) {
using (Html.BeginForm()) {
<h2>Survey #(i)</h2>
<p />
#Html.EditorFor(x => survey.Questions)
<button class='mybutton' type='button' data-personid="#Model.Item1.Id" data-surveyid="#survey.Id" onclick="javascript:SubmitClick()">Click Me</button>
}
i++;
<hr style="background-color:rgb(126, 126, 126);height: 5px" />
}
<hr />
The problem is that when I click the button:
I get runtime error saying that there is no definition of: "SubmitClick".
I don't see any obvious problems in your code, but given that you're handling this in a sub-optimal way, refactoring your code may solve the problem just by improving the setup.
First, don't embed your scripts directly in the view. I understand that you need to include a URL generated via one of the Razor helpers, but what I'm talking about here is using sections so that your scripts get included in a standard location in the document:
So, in your view:
#section Scripts
{
<script>
// your code here
</script>
}
And then in your layout:
<!-- global scripts like jQuery here -->
#RenderSection("Scripts", required: false)
</body>
This ensures that 1) all your JavaScript goes where it should, right before the closing body tag and 2) all your JavaScript gets run after the various global scripts that it will likely depend on (jQuery).
Second, it's usually a bad idea to define things in the global scope, such as you are doing with your SubmitClick function. If another script comes along and defines it's own SubmitClick function in the global scope, then yours gets hosed or vice versa. Instead, you want to use namespaces or closures.
Namespace
var MyNamespace = MyNamespace || {};
MyNamespace.SubmitClick = function () {
...
}
Closure
(function () {
// your code here
})();
Of course, if you use a closure like this, then you SubmitClick function truly won't exist, as it's no longer in the global scope, which brings me to...
Third, don't use the on* HTML attributes. It's far better to bind functionality to elements dynamically, for example:
(function () {
$('.mybutton').on('click', function () {
var pid = $(this).data('personid');
var sid = $(this).data('surveyid');
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { personid: pid, surveyid: sid }, function (data) {
alert('updated');
});
});
})();
Now, you've got zero scope pollution and behavior is bound where behavior is defined, instead of tightly-coupling your HTML and JavaScript.

Categories

Resources